Skip to content

Commit

Permalink
Merge pull request #7 from Yiyiyimu/dev
Browse files Browse the repository at this point in the history
Update to v2.0
  • Loading branch information
Yiyiyimu authored Jan 2, 2021
2 parents a20b09c + 3d16b58 commit 6f93eca
Show file tree
Hide file tree
Showing 4 changed files with 152 additions and 166 deletions.
49 changes: 17 additions & 32 deletions GUI.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,26 +9,22 @@


def Enter():
db, qq, key, msg, n1, n2 = e1.get(), e2.get(), e3.get(), e4.get(), e5.get(
), e6.get()
group = 1 if e7.get() == '私聊' else 2
if (db == "" or qq == "" or (key == "" and msg == "")):
dir, qq_self, qq = e1.get(), e2.get(), e3.get()
group = 1 if e4.get() == '私聊' else 2
if (dir == "" or qq_self == "" or qq == ""):
info.set("信息不完整!")
return ()
info.set("开始导出")
try:
realkey = QQ_History.main(db, qq, key, msg, n1, n2, group)
QQ_History.main(dir, qq_self, qq, group)
except Exception as e:
info.set(repr(e))
return ()
if (key == ""):
keyGet.set(realkey)
info.set("完成")


def SelectPath():
pathTmp = filedialog.askopenfilename()
pathGet.set(pathTmp)
dir = filedialog.askdirectory()
pathGet.set(dir)


def url():
Expand All @@ -46,41 +42,30 @@ def url():

root.title("QQ聊天记录导出")

ttk.Label(root, text="*db文件地址:").grid(row=0, column=0, sticky="e")
ttk.Label(root, text="*com.tencent.mobileqq:").grid(row=0, column=0, sticky="e")
e1 = ttk.Entry(root, textvariable=pathGet)
e1.grid(row=0, column=1)
ttk.Button(root, text="选择", command=SelectPath, width=5).grid(row=0, column=3)

ttk.Label(root, text="*对方QQ号:").grid(row=1, column=0, sticky="e")
ttk.Label(root, text="*自己QQ号:").grid(row=1, column=0, sticky="e")
e2 = ttk.Entry(root)
e2.grid(row=1, column=1, columnspan=3, sticky="ew", pady=3)

ttk.Label(root, text="手机识别码:").grid(row=2, column=0, sticky="e")
e3 = ttk.Entry(root, textvariable=keyGet)
ttk.Label(root, text="*QQ号/群号:").grid(row=2, column=0, sticky="e")
e3 = ttk.Entry(root)
e3.grid(row=2, column=1, columnspan=3, sticky="ew", pady=3)

ttk.Label(root, text="最后一次聊天记录\n(至少六个汉字)").grid(row=3, column=0, sticky="e")
e4 = ttk.Entry(root)
ttk.Label(root, text="私聊/群聊:").grid(row=3, column=0, sticky="e")
e4 = ttk.Combobox(root)
e4['values'] = ('私聊', '群聊')
e4.current(0)
e4.grid(row=3, column=1, columnspan=3, sticky="ew", pady=3)

ttk.Label(root, text="我的名字:").grid(row=4, column=0, sticky="e")
e5 = ttk.Entry(root)
e5.grid(row=4, column=1, columnspan=3, sticky="ew", pady=3)
ttk.Label(root, text="对方名字:").grid(row=5, column=0, sticky="e")
e6 = ttk.Entry(root)
e6.grid(row=5, column=1, columnspan=3, sticky="ew", pady=3)

ttk.Label(root, text="私聊/群聊:").grid(row=6, column=0, sticky="e")
e7 = ttk.Combobox(root)
e7['values'] = ('私聊', '群聊')
e7.current(0)
e7.grid(row=6, column=1, columnspan=3, sticky="ew", pady=3)

root.grid_columnconfigure(2, weight=1)

ttk.Button(root, text="确认", command=Enter).grid(row=7, column=1)
ttk.Button(root, text="确认", command=Enter).grid(row=4, column=1)
l1 = ttk.Label(root, textvariable=info)
l1.grid(row=7, column=1)
l1.grid(row=4, column=1)

tmp = open("tmp.png", "wb+")
tmp.write(base64.b64decode(github_mark))
Expand All @@ -93,4 +78,4 @@ def url():

root.mainloop()

## pyinstaller -F -w -i icon.ico GUI.py
# pyinstaller -F -w -i icon.ico GUI.py
227 changes: 118 additions & 109 deletions QQ_History.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,70 +2,43 @@
import hashlib
import sqlite3
import time
import os
import traceback


class QQoutput():
def __init__(self, db, key, mode, s):
self.key = key # 解密用的密钥
self.c = sqlite3.connect(db).cursor()
def __init__(self, dir, qq_self, qq, mode):
self.dir = dir
self.key = self.get_key() # 解密用的密钥
db = os.path.join(dir, "databases", qq_self + ".db")
self.c1 = sqlite3.connect(db).cursor()
db = os.path.join(dir, "databases", "slowtable_" + qq_self + ".db")
self.c2 = sqlite3.connect(db).cursor()
self.qq_self = qq_self
self.qq = qq
self.mode = mode
self.s = s

def fix(self, data, mode):
# msgdata mode=0
# other mode=1
if (mode == 0):
rowbyte = []
for i in range(0, len(data)):
rowbyte.append(data[i] ^ ord(self.key[i % len(self.key)]))
rowbyte = bytes(rowbyte)
self.num_to_name = {}

def decrypt(self, data):
if type(data) == bytes:
msg = b''
try:
msg = rowbyte.decode(encoding="utf-8")
for i in range(0, len(data)):
msg += bytes([data[i] ^ ord(self.key[i % len(self.key)])])
return msg.decode(encoding="utf-8")
except:
msg = NULL
return msg
elif (mode == 1):
str = ""
return NULL
elif type(data) == str:
msg = ""
try:
for i in range(0, len(data)):
str += chr(ord(data[i]) ^ ord(self.key[i % len(self.key)]))
msg += chr(ord(data[i]) ^ ord(self.key[i % len(self.key)]))
except:
str = NULL
return str

def decode(self, cursor):
for row in cursor:
continue
data = row[0]
MsgEnc = self.s.encode(encoding="utf-8")
KeySet = ""
# for i in range(0,min(len(MsgEnc), len(data))):
for i in range(0, len(MsgEnc)):
KeySet += chr(data[i] ^ MsgEnc[i])
# TO AVOID LOOP
RealKey, restKey = "", ""
for i in range(4, len(KeySet)):
'''
bug WARNING!!
Assuming Key should be longer than 5 digits
To Prevent string loop in single key
Like "121212456"
'''
RealKey, nextKey, restKey = KeySet[0:i], KeySet[i:2 * i], KeySet[
2 * i:len(KeySet)]
KeyLen = len(RealKey)
flagLoop = True
for j in range(KeyLen):
if ((j < len(nextKey) and RealKey[j] != nextKey[j])
or (j < len(restKey) and RealKey[j] != restKey[j])):
flagLoop = False
break
if (flagLoop and j == KeyLen - 1):
break
return RealKey
msg = NULL
return msg
return NULL

def AddEmoji(self, msg):
def add_emoji(self, msg):
pos = msg.find('\x14')
while (pos != -1):
lastpos = pos
Expand All @@ -78,98 +51,134 @@ def AddEmoji(self, msg):
break
return msg

def message(self, num):
def message(self):
# mode=1 friend
# mode=2 troop
num = str(num).encode("utf-8")
num = self.qq.encode("utf-8")
md5num = hashlib.md5(num).hexdigest().upper()
if (self.mode == 1):
execute = "select msgData,senderuin,time from mr_friend_{md5num}_New".format(
md5num=md5num)
elif (self.mode == 2):
execute = "select msgData,senderuin,time from mr_troop_{md5num}_New".format(
cmd = "select msgData,senderuin,time from mr_friend_{md5num}_New".format(
md5num=md5num)
self.get_friends()
else:
print("error mode")
exit(1)
cmd = "select msgData,senderuin,time from mr_troop_{md5num}_New".format(
md5num=md5num)
self.get_troop_members()

cursor = self.c.execute(execute)
if (self.key == "" and len(self.s) >= 5):
self.key = self.decode(cursor)
cursor = self.c.execute(execute)
cursors = self.fill_cursors(cmd)
allmsg = []
for row in cursor:
msgdata = row[0]
if (not msgdata):
continue
uin = row[1]
ltime = time.localtime(row[2])

sendtime = time.strftime("%Y-%m-%d %H:%M:%S", ltime)
msg = self.fix(msgdata, 0)
senderuin = self.fix(uin, 1)

amsg = []
amsg.append(sendtime)
amsg.append(senderuin)
amsg.append(msg)
allmsg.append(amsg)
for cs in cursors:
for row in cs:
msgdata = row[0]
if (not msgdata):
continue
uin = row[1]
ltime = time.localtime(row[2])
sendtime = time.strftime("%Y-%m-%d %H:%M:%S", ltime)

amsg = []
amsg.append(sendtime)
amsg.append(self.decrypt(uin))
amsg.append(self.decrypt(msgdata))
allmsg.append(amsg)
return allmsg

def output(self, num, n1, n2):
name1 = n1 if n1 != "" else "我"
name2 = n2 if n2 != "" else str(num)
file = str(num) + ".html"
def get_friends(self):
cmd = "SELECT uin, remark FROM Friends"
cursors = self.fill_cursors(cmd)
for cs in cursors:
for row in cs:
num = self.decrypt(row[0])
name = self.decrypt(row[1])
self.num_to_name[num] = name

def get_troop_members(self):
cmd = "SELECT troopuin, memberuin, friendnick, troopnick FROM TroopMemberInfo"
cursors = self.fill_cursors(cmd)
for cs in cursors:
for row in cs:
if(self.decrypt(row[0]) != self.qq):
continue
num = self.decrypt(row[1])
name = self.decrypt(row[3]) or self.decrypt(row[2])
self.num_to_name[num] = name

def fill_cursors(self, cmd):
cursors = []
try:
cursors.append(self.c2.execute(cmd))
except:
pass
try:
cursors.append(self.c1.execute(cmd))
except:
pass
return cursors

def output(self):
name1 = "我"
file = str(self.qq) + ".html"
f2 = open(file, "w", encoding="utf-8")
f2.write(
"<head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" /></head>"
)
allmsg = self.message(num)
allmsg = self.message()
f2.write("<div style='white-space: pre-line'>")
for msg in allmsg:
if not msg[2]:
continue
try:
if self.mode == 1:
if (msg[1] == str(num)):
f2.write("<p align='left'>")
f2.write("<font color=\"blue\"><b>")
f2.write(name2)
f2.write("</b></font>-----<font color=\"green\">")
f2.write(msg[0])
f2.write("</font></br>")
else:
f2.write("<p align='right'>")
f2.write("<font color=\"green\">")
f2.write(msg[0])
f2.write("</font>-----<font color=\"blue\"><b>")
f2.write(name1)
f2.write("</font></b></br>")
if (msg[1] == str(self.qq_self)):
f2.write("<p align='right'>")
f2.write("<font color=\"green\">")
f2.write(msg[0])
f2.write("</font>-----<font color=\"blue\"><b>")
f2.write(name1)
f2.write("</font></b></br>")
else:
f2.write("<p align='left'>")
f2.write("<font color=\"blue\"><b>")
f2.write(msg[1])
f2.write(self.num_to_name.get(msg[1]) or msg[1])
f2.write("</b></font>-----<font color=\"green\">")
f2.write(msg[0])
f2.write("</font></br>")
f2.write(self.AddEmoji(msg[2]))
f2.write(self.add_emoji(msg[2]))
f2.write("</br></br>")
f2.write("</p>")
except:
pass
f2.write("</div>")
return self.key

def get_key(self):
self.unify_path()
kc_path = os.path.join(self.dir, "files", "kc")
kc_file = open(kc_path, "r")
return kc_file.read()

def unify_path(self):
if os.path.isdir(os.path.join(self.dir, "f")):
os.rename(os.path.join(self.dir, "f"),
os.path.join(self.dir, "files"))
if os.path.isdir(os.path.join(self.dir, "db")):
os.rename(os.path.join(self.dir, "db"),
os.path.join(self.dir, "databases"))
if os.path.isfile(os.path.join(self.dir, "files", "kc")) == False:
raise OSError(
"File not found. Please report your directory layout.")

def main(db, qq, key, msg, n1, n2, mode):

def main(dir, qq_self, qq, mode):
try:
q = QQoutput(db, key, mode, msg)
return q.output(qq, n1, n2)
q = QQoutput(dir, qq_self, qq, mode)
q.output()
except Exception as e:
with open('log.txt', 'w') as f:
f.write(str(e))
f.write(traceback.format_exc())

err_info = repr(e).split(":")[0] == "OperationalError('no such table"
print(err_info)
print(repr(e))
print(traceback.format_exc())
if (err_info):
raise ValueError("QQ号/私聊群聊选择/db地址/错误")
else:
raise BaseException("Error! See log.txt")
Loading

0 comments on commit 6f93eca

Please sign in to comment.