libchat.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/env python2
  2. # -*- coding: UTF-8 -*-
  3. # File: libchat.py
  4. # Date: Sun Apr 12 21:08:51 2015 +0900
  5. # Author: Yuxin Wu <[email protected]>
  6. import sqlite3
  7. import os
  8. from datetime import datetime
  9. import time
  10. from collections import namedtuple
  11. SOURCE_ID = {'wechat': 0}
  12. NUM_FIELDS = 8
  13. ChatMsgBase = namedtuple('ChatMsgBase',
  14. ['source', 'time', 'sender', 'chatroom',
  15. 'text', 'image', 'sound', 'extra_data'])
  16. """ source: unicode,
  17. time: datetime,
  18. sender: unicode,
  19. chatroom: unicode,
  20. text: unicode,
  21. image: string,
  22. sound: string,
  23. extra_data: string
  24. """
  25. class ChatMsg(ChatMsgBase):
  26. def __repr__(self): # repr must return str?
  27. return "Msg@{}/{}-{}/{}/{}/{}/{}".format(
  28. self.time, self.sender.encode('utf-8'),
  29. self.chatroom.encode('utf-8'),
  30. self.text.encode('utf-8'), 'IMG' if self.image else '',
  31. 'AUD' if self.sound else '', self.extra_data)
  32. class SqliteLibChat(object):
  33. """ Interface for interacting with LibChat database"""
  34. def __init__(self, db_file):
  35. self.db_file = db_file
  36. exist = os.path.isfile(db_file)
  37. self.conn = sqlite3.connect(db_file)
  38. self.conn.text_factory = str # to allow use of raw-byte string
  39. self.c = self.conn.cursor()
  40. if not exist:
  41. self.create()
  42. def create(self):
  43. self.c.execute("""
  44. CREATE TABLE message (
  45. source SMALLINT,
  46. time TEXT,
  47. sender TEXT,
  48. chatroom TEXT,
  49. text TEXT,
  50. image COLLATE BINARY,
  51. sound COLLATE BINARY,
  52. extra_data COLLATE BINARY
  53. )
  54. """)
  55. self.conn.commit()
  56. def _add_msg(self, tp):
  57. assert isinstance(tp, ChatMsg)
  58. self.c.execute(
  59. """INSERT INTO message VALUES ({0})""".format(
  60. ','.join(['?']*NUM_FIELDS)), tp)
  61. def add_msgs(self, msgs):
  62. """ each message is a ChatMsg instance"""
  63. self.c = self.conn.cursor()
  64. for m in msgs:
  65. self._add_msg(SqliteLibChat.prefilter(m))
  66. self.conn.commit()
  67. @staticmethod
  68. def prefilter(msg):
  69. source = msg.source
  70. if isinstance(source, basestring):
  71. source = SOURCE_ID[source]
  72. tm = int(time.mktime(msg[1].timetuple()))
  73. return ChatMsg(source, tm, *msg[2:])
  74. @staticmethod
  75. def postfilter(msg):
  76. # source
  77. text = msg[4].decode('utf-8')
  78. time = datetime.fromtimestamp(int(msg[1]))
  79. return ChatMsg(msg[0], time, msg[2], msg[3],
  80. text=text, image=msg[5],
  81. sound=msg[6], extra_data=msg[7])
  82. def iterate_all_msg(self, predicate=None):
  83. """ predicate: a dict used as SELECT filter
  84. return a generator for all messages
  85. """
  86. if predicate is None:
  87. self.c.execute("SELECT * FROM message")
  88. else:
  89. self.c.execute("SELECT * FROM message WHERE {}".format(
  90. ' AND '.join(["{} = {}".format(k, v)
  91. for k, v in predicate.iteritems()])))
  92. for row in self.c.fetchall():
  93. yield ChatMsg(*SqliteLibChat.postfilter(row))
  94. if __name__ == '__main__':
  95. db = SqliteLibChat(os.path.join(
  96. os.path.dirname(__file__), './message.db'))
  97. #msg = ChatMsg(-1, 1000, 'me', 'room', 'hello', '\x01\x02\x03', '', '')
  98. #db.add_msgs([msg])
  99. for k in db.iterate_all_msg():
  100. from IPython import embed; embed()
  101. print k