2023-11-15 23:53:39 +08:00
|
|
|
|
import os.path
|
2023-12-03 21:25:50 +08:00
|
|
|
|
import random
|
2023-11-15 23:53:39 +08:00
|
|
|
|
import sqlite3
|
2023-11-17 21:34:22 +08:00
|
|
|
|
import threading
|
2023-12-01 22:37:45 +08:00
|
|
|
|
import traceback
|
2023-12-26 23:49:53 +08:00
|
|
|
|
|
2024-01-02 00:39:45 +08:00
|
|
|
|
from app.DataBase.hard_link import parseBytes
|
2023-12-01 22:37:45 +08:00
|
|
|
|
from app.log import logger
|
2023-12-20 18:19:17 +08:00
|
|
|
|
from app.util.compress_content import parser_reply
|
2023-12-29 21:50:17 +08:00
|
|
|
|
from app.util.protocbuf.msg_pb2 import MessageBytesExtra
|
2023-12-01 22:37:45 +08:00
|
|
|
|
|
2023-11-29 21:23:44 +08:00
|
|
|
|
db_path = "./app/Database/Msg/MSG.db"
|
2023-11-17 21:34:22 +08:00
|
|
|
|
lock = threading.Lock()
|
2023-11-25 00:20:35 +08:00
|
|
|
|
|
2023-11-16 22:39:59 +08:00
|
|
|
|
|
2023-11-25 00:20:35 +08:00
|
|
|
|
def is_database_exist():
|
2023-11-29 21:23:44 +08:00
|
|
|
|
return os.path.exists(db_path)
|
2023-11-25 00:20:35 +08:00
|
|
|
|
|
|
|
|
|
|
2023-12-01 22:37:45 +08:00
|
|
|
|
def singleton(cls):
|
|
|
|
|
_instance = {}
|
|
|
|
|
|
|
|
|
|
def inner():
|
|
|
|
|
if cls not in _instance:
|
|
|
|
|
_instance[cls] = cls()
|
|
|
|
|
return _instance[cls]
|
|
|
|
|
|
|
|
|
|
return inner
|
|
|
|
|
|
|
|
|
|
|
2023-12-03 21:25:50 +08:00
|
|
|
|
class MsgType:
|
|
|
|
|
TEXT = 1
|
|
|
|
|
IMAGE = 3
|
|
|
|
|
EMOJI = 47
|
|
|
|
|
|
|
|
|
|
|
2023-12-01 22:37:45 +08:00
|
|
|
|
class Msg:
|
|
|
|
|
def __init__(self):
|
|
|
|
|
self.DB = None
|
|
|
|
|
self.cursor = None
|
|
|
|
|
self.open_flag = False
|
|
|
|
|
self.init_database()
|
|
|
|
|
|
2023-12-03 21:25:50 +08:00
|
|
|
|
def init_database(self, path=None):
|
|
|
|
|
global db_path
|
2023-12-01 22:37:45 +08:00
|
|
|
|
if not self.open_flag:
|
2023-12-03 21:25:50 +08:00
|
|
|
|
if path:
|
|
|
|
|
db_path = path
|
2023-12-01 22:37:45 +08:00
|
|
|
|
if os.path.exists(db_path):
|
|
|
|
|
self.DB = sqlite3.connect(db_path, check_same_thread=False)
|
|
|
|
|
# '''创建游标'''
|
|
|
|
|
self.cursor = self.DB.cursor()
|
|
|
|
|
self.open_flag = True
|
|
|
|
|
if lock.locked():
|
|
|
|
|
lock.release()
|
|
|
|
|
|
2023-12-29 21:50:17 +08:00
|
|
|
|
def add_sender(self, messages):
|
|
|
|
|
"""
|
|
|
|
|
@param messages:
|
|
|
|
|
@return:
|
|
|
|
|
"""
|
|
|
|
|
new_messages = []
|
|
|
|
|
for message in messages:
|
|
|
|
|
is_sender = message[4]
|
|
|
|
|
wxid = ''
|
|
|
|
|
if is_sender:
|
|
|
|
|
pass
|
|
|
|
|
else:
|
|
|
|
|
msgbytes = MessageBytesExtra()
|
|
|
|
|
msgbytes.ParseFromString(message[10])
|
|
|
|
|
for tmp in msgbytes.message2:
|
|
|
|
|
if tmp.field1 != 1:
|
|
|
|
|
continue
|
|
|
|
|
wxid = tmp.field2
|
|
|
|
|
new_message = (*message, wxid)
|
|
|
|
|
new_messages.append(new_message)
|
|
|
|
|
return new_messages
|
|
|
|
|
|
2023-12-01 22:37:45 +08:00
|
|
|
|
def get_messages(self, username_):
|
2023-12-29 21:50:17 +08:00
|
|
|
|
"""
|
2023-12-17 18:33:06 +08:00
|
|
|
|
return list
|
|
|
|
|
a[0]: localId,
|
|
|
|
|
a[1]: talkerId, (和strtalker对应的,不是群聊信息发送人)
|
|
|
|
|
a[2]: type,
|
|
|
|
|
a[3]: subType,
|
|
|
|
|
a[4]: is_sender,
|
|
|
|
|
a[5]: timestamp,
|
|
|
|
|
a[6]: status, (没啥用)
|
|
|
|
|
a[7]: str_content,
|
|
|
|
|
a[8]: str_time, (格式化的时间)
|
|
|
|
|
a[9]: msgSvrId,
|
|
|
|
|
a[10]: BytesExtra,
|
|
|
|
|
a[11]: CompressContent,
|
2023-12-29 21:50:17 +08:00
|
|
|
|
"""
|
2023-12-01 22:37:45 +08:00
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
sql = '''
|
2023-12-11 21:17:29 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
|
2023-11-17 21:34:22 +08:00
|
|
|
|
from MSG
|
2023-12-07 11:57:08 +08:00
|
|
|
|
where StrTalker=?
|
2023-12-01 22:37:45 +08:00
|
|
|
|
order by CreateTime
|
2023-11-17 21:34:22 +08:00
|
|
|
|
'''
|
2023-12-01 22:37:45 +08:00
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_])
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
2023-12-11 23:04:59 +08:00
|
|
|
|
return result
|
2023-12-29 21:50:17 +08:00
|
|
|
|
# result.sort(key=lambda x: x[5])
|
|
|
|
|
# return self.add_sender(result)
|
2023-12-01 22:37:45 +08:00
|
|
|
|
|
|
|
|
|
def get_messages_all(self):
|
|
|
|
|
sql = '''
|
2023-12-11 21:17:29 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,StrTalker,Reserved1,CompressContent
|
2023-12-01 22:37:45 +08:00
|
|
|
|
from MSG
|
|
|
|
|
order by CreateTime
|
|
|
|
|
'''
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql)
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
result.sort(key=lambda x: x[5])
|
2023-12-11 23:04:59 +08:00
|
|
|
|
return result
|
2023-12-01 22:37:45 +08:00
|
|
|
|
|
2023-12-06 23:54:12 +08:00
|
|
|
|
def get_messages_length(self):
|
|
|
|
|
sql = '''
|
|
|
|
|
select count(*)
|
2023-12-13 14:41:07 +08:00
|
|
|
|
group by MsgSvrID
|
2023-12-06 23:54:12 +08:00
|
|
|
|
from MSG
|
|
|
|
|
'''
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql)
|
|
|
|
|
result = self.cursor.fetchone()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
result = None
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result[0]
|
|
|
|
|
|
2023-12-01 22:37:45 +08:00
|
|
|
|
def get_message_by_num(self, username_, local_id):
|
|
|
|
|
sql = '''
|
2023-12-11 21:17:29 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
|
2023-12-01 22:37:45 +08:00
|
|
|
|
from MSG
|
2023-12-07 11:57:08 +08:00
|
|
|
|
where StrTalker = ? and localId < ?
|
2023-12-01 22:37:45 +08:00
|
|
|
|
order by CreateTime desc
|
2023-12-07 20:54:28 +08:00
|
|
|
|
limit 20
|
2023-12-01 22:37:45 +08:00
|
|
|
|
'''
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_, local_id])
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
# result.sort(key=lambda x: x[5])
|
2023-12-11 23:04:59 +08:00
|
|
|
|
return result
|
2023-12-01 22:37:45 +08:00
|
|
|
|
|
2023-12-20 22:23:13 +08:00
|
|
|
|
def get_messages_by_type(self, username_, type_, year_='all'):
|
2023-12-03 21:25:50 +08:00
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
2023-12-20 22:23:13 +08:00
|
|
|
|
if year_ == 'all':
|
2023-12-10 22:00:19 +08:00
|
|
|
|
sql = '''
|
2023-12-20 22:23:13 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
|
|
|
|
|
from MSG
|
2023-12-22 22:42:24 +08:00
|
|
|
|
where StrTalker=? and Type=?
|
2023-12-20 22:23:13 +08:00
|
|
|
|
order by CreateTime
|
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_, type_])
|
2023-12-22 22:42:24 +08:00
|
|
|
|
result = self.cursor.fetchall()
|
2023-12-20 22:23:13 +08:00
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
2023-12-10 22:00:19 +08:00
|
|
|
|
else:
|
|
|
|
|
sql = '''
|
2023-12-20 22:23:13 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra,CompressContent
|
|
|
|
|
from MSG
|
|
|
|
|
where StrTalker=? and Type=? and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?
|
|
|
|
|
order by CreateTime
|
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
self.cursor.execute(sql, [username_, type_, year_])
|
2023-12-20 22:23:13 +08:00
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
result = self.cursor.fetchall()
|
2023-12-12 10:38:43 +08:00
|
|
|
|
return result
|
2023-12-03 21:25:50 +08:00
|
|
|
|
|
2023-12-20 22:23:13 +08:00
|
|
|
|
def get_messages_by_keyword(self, username_, keyword, num=5, max_len=10, year_='all'):
|
2023-12-03 21:25:50 +08:00
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
2023-12-20 22:23:13 +08:00
|
|
|
|
sql = f'''
|
2023-12-07 21:34:27 +08:00
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID,BytesExtra
|
2023-12-03 21:25:50 +08:00
|
|
|
|
from MSG
|
2023-12-07 11:57:08 +08:00
|
|
|
|
where StrTalker=? and Type=1 and LENGTH(StrContent)<? and StrContent like ?
|
2023-12-20 22:23:13 +08:00
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
2023-12-03 21:25:50 +08:00
|
|
|
|
order by CreateTime desc
|
|
|
|
|
'''
|
|
|
|
|
temp = []
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-20 22:23:13 +08:00
|
|
|
|
self.cursor.execute(sql, [username_, max_len, f'%{keyword}%'] if year_ == "all" else [username_, max_len,
|
|
|
|
|
f'%{keyword}%',
|
|
|
|
|
year_])
|
2023-12-03 21:25:50 +08:00
|
|
|
|
messages = self.cursor.fetchall()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
if len(messages) > 5:
|
|
|
|
|
messages = random.sample(messages, num)
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
for msg in messages:
|
|
|
|
|
local_id = msg[0]
|
|
|
|
|
is_send = msg[4]
|
|
|
|
|
sql = '''
|
|
|
|
|
select localId,TalkerId,Type,SubType,IsSender,CreateTime,Status,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,MsgSvrID
|
|
|
|
|
from MSG
|
|
|
|
|
where localId > ? and StrTalker=? and Type=1 and IsSender=?
|
|
|
|
|
limit 1
|
|
|
|
|
'''
|
|
|
|
|
self.cursor.execute(sql, [local_id, username_, 1 - is_send])
|
|
|
|
|
temp.append((msg, self.cursor.fetchone()))
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
res = []
|
|
|
|
|
for dialog in temp:
|
|
|
|
|
msg1 = dialog[0]
|
|
|
|
|
msg2 = dialog[1]
|
2023-12-13 20:45:53 +08:00
|
|
|
|
try:
|
|
|
|
|
res.append((
|
|
|
|
|
(msg1[4], msg1[5], msg1[7].split(keyword), msg1[8]),
|
|
|
|
|
(msg2[4], msg2[5], msg2[7], msg2[8])
|
|
|
|
|
))
|
|
|
|
|
except TypeError:
|
|
|
|
|
res.append((
|
|
|
|
|
('', '', ['', ''], ''),
|
|
|
|
|
('', '', '', '')
|
|
|
|
|
))
|
2023-12-20 22:23:13 +08:00
|
|
|
|
"""
|
|
|
|
|
返回值为一个列表,每个列表元素是一个对话
|
|
|
|
|
每个对话是一个元组数据
|
|
|
|
|
('is_send','时间戳','以关键词为分割符的消息内容','格式化时间')
|
|
|
|
|
"""
|
2023-12-03 21:25:50 +08:00
|
|
|
|
return res
|
2023-12-17 13:15:49 +08:00
|
|
|
|
|
2023-12-16 20:06:43 +08:00
|
|
|
|
def get_contact(self, contacts):
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
sql = '''select StrTalker, MAX(CreateTime) from MSG group by StrTalker'''
|
|
|
|
|
self.cursor.execute(sql)
|
|
|
|
|
res = self.cursor.fetchall()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
res = {StrTalker: CreateTime for StrTalker, CreateTime in res}
|
|
|
|
|
contacts = [list(cur_contact) for cur_contact in contacts]
|
|
|
|
|
for i, cur_contact in enumerate(contacts):
|
|
|
|
|
if cur_contact[0] in res:
|
|
|
|
|
contacts[i].append(res[cur_contact[0]])
|
|
|
|
|
else:
|
|
|
|
|
contacts[i].append(0)
|
|
|
|
|
contacts.sort(key=lambda cur_contact: cur_contact[-1], reverse=True)
|
|
|
|
|
return contacts
|
2023-12-17 13:15:49 +08:00
|
|
|
|
|
2023-12-10 22:00:19 +08:00
|
|
|
|
def get_messages_by_days(self, username_, is_Annual_report_=False, year_='2023'):
|
|
|
|
|
if is_Annual_report_:
|
|
|
|
|
sql = '''
|
|
|
|
|
SELECT strftime('%Y-%m-%d',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
|
2023-12-13 14:41:07 +08:00
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
|
|
|
|
WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?
|
|
|
|
|
)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
group by days
|
|
|
|
|
'''
|
|
|
|
|
else:
|
|
|
|
|
sql = '''
|
|
|
|
|
SELECT strftime('%Y-%m-%d',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
|
2023-12-13 14:41:07 +08:00
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
|
|
|
|
WHERE StrTalker = ?
|
|
|
|
|
)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
group by days
|
|
|
|
|
'''
|
2023-12-07 17:22:44 +08:00
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
2023-12-07 16:51:15 +08:00
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
if is_Annual_report_:
|
|
|
|
|
self.cursor.execute(sql, [username_, year_])
|
|
|
|
|
else:
|
|
|
|
|
self.cursor.execute(sql, [username_])
|
2023-12-07 16:51:15 +08:00
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
2023-12-07 20:35:51 +08:00
|
|
|
|
|
2023-12-10 22:00:19 +08:00
|
|
|
|
def get_messages_by_month(self, username_, is_Annual_report_=False, year_='2023'):
|
|
|
|
|
if is_Annual_report_:
|
|
|
|
|
sql = '''
|
2023-12-13 14:41:07 +08:00
|
|
|
|
SELECT strftime('%Y-%m',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
|
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
|
|
|
|
WHERE StrTalker = ? AND strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?
|
|
|
|
|
)
|
|
|
|
|
group by days
|
2023-12-10 22:00:19 +08:00
|
|
|
|
'''
|
|
|
|
|
else:
|
|
|
|
|
sql = '''
|
2023-12-07 19:26:37 +08:00
|
|
|
|
SELECT strftime('%Y-%m',CreateTime,'unixepoch','localtime') as days,count(MsgSvrID)
|
2023-12-13 14:41:07 +08:00
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
|
|
|
|
WHERE StrTalker = ?
|
|
|
|
|
)
|
2023-12-07 19:26:37 +08:00
|
|
|
|
group by days
|
|
|
|
|
'''
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
if is_Annual_report_:
|
|
|
|
|
self.cursor.execute(sql, [username_, year_])
|
|
|
|
|
else:
|
|
|
|
|
self.cursor.execute(sql, [username_])
|
2023-12-07 19:26:37 +08:00
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
# result.sort(key=lambda x: x[5])
|
|
|
|
|
return result
|
2023-12-07 16:51:15 +08:00
|
|
|
|
|
2023-12-20 22:23:13 +08:00
|
|
|
|
def get_messages_by_hour(self, username_, year_='all'):
|
|
|
|
|
result = []
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return result
|
|
|
|
|
if year_ == 'all':
|
2023-12-10 22:00:19 +08:00
|
|
|
|
sql = '''
|
2023-12-13 14:41:07 +08:00
|
|
|
|
SELECT strftime('%H:00',CreateTime,'unixepoch','localtime') as hours,count(MsgSvrID)
|
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
2023-12-20 22:23:13 +08:00
|
|
|
|
where StrTalker = ?
|
2023-12-13 14:41:07 +08:00
|
|
|
|
)
|
|
|
|
|
group by hours
|
2023-12-20 22:23:13 +08:00
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_])
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
result = self.cursor.fetchall()
|
2023-12-10 22:00:19 +08:00
|
|
|
|
else:
|
|
|
|
|
sql = '''
|
2023-12-07 19:48:59 +08:00
|
|
|
|
SELECT strftime('%H:00',CreateTime,'unixepoch','localtime') as hours,count(MsgSvrID)
|
2023-12-13 14:41:07 +08:00
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
2023-12-20 22:23:13 +08:00
|
|
|
|
where StrTalker = ? and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?
|
2023-12-13 14:41:07 +08:00
|
|
|
|
)
|
2023-12-07 19:48:59 +08:00
|
|
|
|
group by hours
|
2023-12-20 22:23:13 +08:00
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-10 22:00:19 +08:00
|
|
|
|
self.cursor.execute(sql, [username_, year_])
|
2023-12-20 22:23:13 +08:00
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
result = self.cursor.fetchall()
|
2023-12-07 19:48:59 +08:00
|
|
|
|
return result
|
2023-12-07 16:51:15 +08:00
|
|
|
|
|
2023-12-05 00:13:20 +08:00
|
|
|
|
def get_first_time_of_message(self, username_):
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
sql = '''
|
|
|
|
|
select StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime
|
|
|
|
|
from MSG
|
2023-12-07 11:57:08 +08:00
|
|
|
|
where StrTalker=?
|
2023-12-05 00:13:20 +08:00
|
|
|
|
order by CreateTime
|
|
|
|
|
limit 1
|
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_])
|
|
|
|
|
result = self.cursor.fetchone()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
|
|
|
|
|
2023-12-20 22:23:13 +08:00
|
|
|
|
def get_latest_time_of_message(self, username_, year_='all'):
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
sql = f'''
|
|
|
|
|
SELECT isSender,StrContent,strftime('%Y-%m-%d %H:%M:%S',CreateTime,'unixepoch','localtime') as StrTime,
|
|
|
|
|
strftime('%H:%M:%S', CreateTime,'unixepoch','localtime') as hour
|
|
|
|
|
FROM MSG
|
|
|
|
|
WHERE StrTalker = ? AND Type=1 AND
|
|
|
|
|
hour BETWEEN '00:00:00' AND '05:00:00'
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
ORDER BY hour DESC
|
|
|
|
|
LIMIT 20;
|
|
|
|
|
'''
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [username_, year_] if year_ != "all" else [username_])
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
if not result:
|
|
|
|
|
return []
|
|
|
|
|
res = []
|
|
|
|
|
is_sender = result[0][0]
|
|
|
|
|
res.append(result[0])
|
|
|
|
|
for msg in result[1:]:
|
|
|
|
|
if msg[0] != is_sender:
|
|
|
|
|
res.append(msg)
|
|
|
|
|
break
|
|
|
|
|
return res
|
|
|
|
|
|
2023-12-20 18:19:17 +08:00
|
|
|
|
def get_send_messages_type_number(self, year_="all") -> list:
|
|
|
|
|
"""
|
|
|
|
|
统计自己发的各类型消息条数,按条数降序,精确到subtype\n
|
|
|
|
|
return [(type_1, subtype_1, number_1), (type_2, subtype_2, number_2), ...]\n
|
|
|
|
|
be like [(1, 0, 71481), (3, 0, 6686), (49, 57, 3887), ..., (10002, 0, 1)]
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
sql = f"""
|
|
|
|
|
SELECT type, subtype, Count(MsgSvrID)
|
|
|
|
|
from MSG
|
|
|
|
|
where isSender = 1
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
group by type, subtype
|
|
|
|
|
order by Count(MsgSvrID) desc
|
|
|
|
|
"""
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [year_] if year_ != "all" else [])
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
|
|
|
|
|
2023-12-20 22:23:13 +08:00
|
|
|
|
def get_messages_number(self, username_, year_="all") -> int:
|
|
|
|
|
sql = f"""
|
|
|
|
|
SELECT Count(MsgSvrID)
|
|
|
|
|
from MSG
|
|
|
|
|
where StrTalker = ?
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
group by type, subtype
|
|
|
|
|
order by Count(MsgSvrID) desc
|
|
|
|
|
"""
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
2023-12-29 21:50:17 +08:00
|
|
|
|
self.cursor.execute(sql, [username_, year_] if year_ != "all" else [username_])
|
2023-12-20 22:23:13 +08:00
|
|
|
|
result = self.cursor.fetchone()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result[0] if result else 0
|
|
|
|
|
|
2023-12-20 18:19:17 +08:00
|
|
|
|
def get_chatted_top_contacts(self, year_="all", contain_chatroom=False, top_n=10) -> list:
|
|
|
|
|
"""
|
|
|
|
|
统计聊天最多的 n 个联系人(默认不包含群组),按条数降序\n
|
|
|
|
|
return [(wxid_1, number_1), (wxid_2, number_2), ...]
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
sql = f"""
|
|
|
|
|
SELECT strtalker, Count(MsgSvrID)
|
|
|
|
|
from MSG
|
|
|
|
|
where strtalker != "filehelper" and strtalker != "notifymessage" and strtalker not like "gh_%"
|
|
|
|
|
{"and strtalker not like '%@chatroom'" if not contain_chatroom else ""}
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
group by strtalker
|
|
|
|
|
order by Count(MsgSvrID) desc
|
|
|
|
|
limit {top_n}
|
|
|
|
|
"""
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [year_] if year_ != "all" else [])
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
2023-12-20 22:23:13 +08:00
|
|
|
|
|
2023-12-20 18:19:17 +08:00
|
|
|
|
def get_send_messages_length(self, year_="all") -> int:
|
|
|
|
|
"""
|
|
|
|
|
统计自己总共发消息的字数,包含type=1的文本和type=49,subtype=57里面自己发的文本
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
sql_type_1 = f"""
|
|
|
|
|
SELECT sum(length(strContent))
|
|
|
|
|
from MSG
|
|
|
|
|
where isSender = 1 and type = 1
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
"""
|
|
|
|
|
sql_type_49 = f"""
|
|
|
|
|
SELECT CompressContent
|
|
|
|
|
from MSG
|
|
|
|
|
where isSender = 1 and type = 49 and subtype = 57
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
"""
|
|
|
|
|
sum_type_1 = None
|
|
|
|
|
result_type_49 = None
|
|
|
|
|
sum_type_49 = 0
|
|
|
|
|
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql_type_1, [year_] if year_ != "all" else [])
|
|
|
|
|
sum_type_1 = self.cursor.fetchall()[0][0]
|
|
|
|
|
self.cursor.execute(sql_type_49, [year_] if year_ != "all" else [])
|
|
|
|
|
result_type_49 = self.cursor.fetchall()
|
|
|
|
|
for message in result_type_49:
|
|
|
|
|
message = message[0]
|
|
|
|
|
content = parser_reply(message)
|
|
|
|
|
if content["is_error"]:
|
|
|
|
|
continue
|
|
|
|
|
sum_type_49 += len(content["title"])
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return sum_type_1 + sum_type_49
|
|
|
|
|
|
|
|
|
|
def get_send_messages_number_sum(self, year_="all") -> int:
|
|
|
|
|
"""统计自己总共发了多少条消息"""
|
|
|
|
|
|
|
|
|
|
sql = f"""
|
|
|
|
|
SELECT count(MsgSvrID)
|
|
|
|
|
from MSG
|
|
|
|
|
where isSender = 1
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
"""
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [year_] if year_ != "all" else [])
|
|
|
|
|
result = self.cursor.fetchall()[0][0]
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
|
def get_send_messages_number_by_hour(self, year_="all"):
|
|
|
|
|
"""
|
|
|
|
|
统计每个(小时)时段自己总共发了多少消息,从最多到最少排序\n
|
|
|
|
|
return be like [('23', 9526), ('00', 7890), ('22', 7600), ..., ('05', 29)]
|
|
|
|
|
"""
|
|
|
|
|
sql = f"""
|
|
|
|
|
SELECT strftime('%H', CreateTime, 'unixepoch', 'localtime') as hour,count(MsgSvrID)
|
|
|
|
|
from (
|
|
|
|
|
SELECT MsgSvrID, CreateTime
|
|
|
|
|
FROM MSG
|
|
|
|
|
where isSender = 1
|
|
|
|
|
{"and strftime('%Y', CreateTime, 'unixepoch', 'localtime') = ?" if year_ != "all" else ""}
|
|
|
|
|
)
|
|
|
|
|
group by hour
|
|
|
|
|
order by count(MsgSvrID) desc
|
|
|
|
|
"""
|
|
|
|
|
result = None
|
|
|
|
|
if not self.open_flag:
|
|
|
|
|
return None
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.cursor.execute(sql, [year_] if year_ != "all" else [])
|
|
|
|
|
result = self.cursor.fetchall()
|
|
|
|
|
except sqlite3.DatabaseError:
|
|
|
|
|
logger.error(f'{traceback.format_exc()}\n数据库损坏请删除msg文件夹重试')
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
return result
|
|
|
|
|
|
2023-12-01 22:37:45 +08:00
|
|
|
|
def close(self):
|
|
|
|
|
if self.open_flag:
|
|
|
|
|
try:
|
|
|
|
|
lock.acquire(True)
|
|
|
|
|
self.open_flag = False
|
|
|
|
|
self.DB.close()
|
|
|
|
|
finally:
|
|
|
|
|
lock.release()
|
|
|
|
|
|
|
|
|
|
def __del__(self):
|
|
|
|
|
self.close()
|
2023-11-16 22:39:59 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
2023-12-20 18:19:17 +08:00
|
|
|
|
db_path = "./app/database/Msg/MSG.db"
|
2023-12-01 22:37:45 +08:00
|
|
|
|
msg = Msg()
|
|
|
|
|
msg.init_database()
|
2023-12-29 21:50:17 +08:00
|
|
|
|
wxid = 'wxid_0o18ef858vnu22'
|
|
|
|
|
wxid = '24521163022@chatroom'
|
2024-01-02 00:39:45 +08:00
|
|
|
|
wxid = 'wxid_vtz9jk9ulzjt22' # si
|
|
|
|
|
print()
|
|
|
|
|
from app.util import compress_content
|
|
|
|
|
import xml.etree.ElementTree as ET
|
|
|
|
|
msgs = msg.get_messages(wxid)
|
|
|
|
|
|
|
|
|
|
for msg in msgs:
|
|
|
|
|
if msg[2]==49 and msg[3]==5:
|
|
|
|
|
xml = compress_content.decompress_CompressContent(msg[11])
|
|
|
|
|
root = ET.XML(xml)
|
|
|
|
|
appmsg = root.find('appmsg')
|
|
|
|
|
title = appmsg.find('title').text
|
|
|
|
|
des = appmsg.find('des').text
|
|
|
|
|
url = appmsg.find('url').text
|
|
|
|
|
appinfo = root.find('appinfo')
|
|
|
|
|
show_display_name = appmsg.find('sourcedisplayname')
|
|
|
|
|
if show_display_name is not None:
|
|
|
|
|
show_display_name = show_display_name.text
|
|
|
|
|
else:
|
|
|
|
|
show_display_name = appinfo.find('appname').text
|
|
|
|
|
print(title, des, url, show_display_name)
|
|
|
|
|
bytesDict = parseBytes(msg[10])
|
|
|
|
|
for msginfo in bytesDict[3]:
|
|
|
|
|
print(msginfo)
|
|
|
|
|
if msginfo[1][1][1] == 3:
|
|
|
|
|
thumb = msginfo[1][2][1]
|
|
|
|
|
print(thumb)
|
|
|
|
|
if msginfo[1][1][1] == 4:
|
|
|
|
|
app_logo = msginfo[1][2][1]
|
|
|
|
|
print('logo',app_logo)
|