mirror of
https://github.com/KnugiHK/WhatsApp-Chat-Exporter.git
synced 2026-04-26 16:01:33 +00:00
Implement export TXT chat #22
This commit is contained in:
@@ -2,7 +2,8 @@ try:
|
|||||||
from .__init__ import __version__
|
from .__init__ import __version__
|
||||||
except ImportError:
|
except ImportError:
|
||||||
from Whatsapp_Chat_Exporter.__init__ import __version__
|
from Whatsapp_Chat_Exporter.__init__ import __version__
|
||||||
from Whatsapp_Chat_Exporter import extract, extract_iphone
|
import glob
|
||||||
|
from Whatsapp_Chat_Exporter import extract, extract_exported, extract_iphone
|
||||||
from Whatsapp_Chat_Exporter import extract_iphone_media
|
from Whatsapp_Chat_Exporter import extract_iphone_media
|
||||||
from Whatsapp_Chat_Exporter.data_model import ChatStore
|
from Whatsapp_Chat_Exporter.data_model import ChatStore
|
||||||
from Whatsapp_Chat_Exporter.utility import Crypt, check_update
|
from Whatsapp_Chat_Exporter.utility import Crypt, check_update
|
||||||
@@ -93,7 +94,6 @@ def main():
|
|||||||
help="Path to custom HTML template"
|
help="Path to custom HTML template"
|
||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-e",
|
|
||||||
"--embedded",
|
"--embedded",
|
||||||
dest="embedded",
|
dest="embedded",
|
||||||
default=False,
|
default=False,
|
||||||
@@ -147,6 +147,20 @@ def main():
|
|||||||
action='store_true',
|
action='store_true',
|
||||||
help="Check for updates (require Internet access)"
|
help="Check for updates (require Internet access)"
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-e",
|
||||||
|
"--exported",
|
||||||
|
dest="exported",
|
||||||
|
default=None,
|
||||||
|
help="Path to exported chat file"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--assume-first-as-me",
|
||||||
|
dest="assume_first_as_me",
|
||||||
|
default=False,
|
||||||
|
action='store_true',
|
||||||
|
help="Assume the first message in a chat as sent by me (must be used together with -e)"
|
||||||
|
)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Check for updates
|
# Check for updates
|
||||||
@@ -154,10 +168,10 @@ def main():
|
|||||||
exit(check_update())
|
exit(check_update())
|
||||||
|
|
||||||
# Sanity checks
|
# Sanity checks
|
||||||
if args.android and args.iphone:
|
if args.android and args.iphone and args.exported:
|
||||||
print("You must define only one device type.")
|
print("You must define only one device type.")
|
||||||
exit(1)
|
exit(1)
|
||||||
if not args.android and not args.iphone:
|
if not args.android and not args.iphone and not args.exported:
|
||||||
print("You must define the device type.")
|
print("You must define the device type.")
|
||||||
exit(1)
|
exit(1)
|
||||||
if args.no_html and not args.json:
|
if args.no_html and not args.json:
|
||||||
@@ -216,7 +230,6 @@ def main():
|
|||||||
with sqlite3.connect(contact_db) as db:
|
with sqlite3.connect(contact_db) as db:
|
||||||
db.row_factory = sqlite3.Row
|
db.row_factory = sqlite3.Row
|
||||||
contacts(db, data)
|
contacts(db, data)
|
||||||
|
|
||||||
elif args.iphone:
|
elif args.iphone:
|
||||||
import sys
|
import sys
|
||||||
if "--iphone" in sys.argv:
|
if "--iphone" in sys.argv:
|
||||||
@@ -241,14 +254,49 @@ def main():
|
|||||||
if args.media is None:
|
if args.media is None:
|
||||||
args.media = "Message"
|
args.media = "Message"
|
||||||
|
|
||||||
if os.path.isfile(msg_db):
|
if not args.exported:
|
||||||
with sqlite3.connect(msg_db) as db:
|
if os.path.isfile(msg_db):
|
||||||
db.row_factory = sqlite3.Row
|
with sqlite3.connect(msg_db) as db:
|
||||||
messages(db, data)
|
db.row_factory = sqlite3.Row
|
||||||
media(db, data, args.media)
|
messages(db, data)
|
||||||
vcard(db, data)
|
media(db, data, args.media)
|
||||||
|
vcard(db, data)
|
||||||
|
if not args.no_html:
|
||||||
|
create_html(
|
||||||
|
data,
|
||||||
|
args.output,
|
||||||
|
args.template,
|
||||||
|
args.embedded,
|
||||||
|
args.offline,
|
||||||
|
args.size
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"The message database does not exist. You may specify the path "
|
||||||
|
"to database file with option -d or check your provided path."
|
||||||
|
)
|
||||||
|
exit(2)
|
||||||
|
|
||||||
|
if os.path.isdir(args.media):
|
||||||
|
if os.path.isdir(f"{args.output}/{args.media}"):
|
||||||
|
print("Media directory already exists in output directory. Skipping...")
|
||||||
|
else:
|
||||||
|
if not args.move_media:
|
||||||
|
if os.path.isdir(f"{args.output}/WhatsApp"):
|
||||||
|
print("WhatsApp directory already exists in output directory. Skipping...")
|
||||||
|
else:
|
||||||
|
print("Copying media directory...")
|
||||||
|
shutil.copytree(args.media, f"{args.output}/WhatsApp")
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
shutil.move(args.media, f"{args.output}/")
|
||||||
|
except PermissionError:
|
||||||
|
print("Cannot remove original WhatsApp directory. "
|
||||||
|
"Perhaps the directory is opened?")
|
||||||
|
else:
|
||||||
|
extract_exported.messages(args.exported, data, args.assume_first_as_me)
|
||||||
if not args.no_html:
|
if not args.no_html:
|
||||||
create_html(
|
extract.create_html(
|
||||||
data,
|
data,
|
||||||
args.output,
|
args.output,
|
||||||
args.template,
|
args.template,
|
||||||
@@ -256,29 +304,8 @@ def main():
|
|||||||
args.offline,
|
args.offline,
|
||||||
args.size
|
args.size
|
||||||
)
|
)
|
||||||
else:
|
for file in glob.glob(r'*.*'):
|
||||||
print(
|
shutil.copy(file, args.output)
|
||||||
"The message database does not exist. You may specify the path "
|
|
||||||
"to database file with option -d or check your provided path."
|
|
||||||
)
|
|
||||||
exit(2)
|
|
||||||
|
|
||||||
if os.path.isdir(args.media):
|
|
||||||
if os.path.isdir(f"{args.output}/{args.media}"):
|
|
||||||
print("Media directory already exists in output directory. Skipping...")
|
|
||||||
else:
|
|
||||||
if not args.move_media:
|
|
||||||
if os.path.isdir(f"{args.output}/WhatsApp"):
|
|
||||||
print("WhatsApp directory already exists in output directory. Skipping...")
|
|
||||||
else:
|
|
||||||
print("Copying media directory...")
|
|
||||||
shutil.copytree(args.media, f"{args.output}/WhatsApp")
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
shutil.move(args.media, f"{args.output}/")
|
|
||||||
except PermissionError:
|
|
||||||
print("Cannot remove original WhatsApp directory. "
|
|
||||||
"Perhaps the directory is opened?")
|
|
||||||
|
|
||||||
if args.json:
|
if args.json:
|
||||||
if isinstance(data[next(iter(data))], ChatStore):
|
if isinstance(data[next(iter(data))], ChatStore):
|
||||||
|
|||||||
@@ -30,10 +30,13 @@ class ChatStore():
|
|||||||
|
|
||||||
|
|
||||||
class Message():
|
class Message():
|
||||||
def __init__(self, from_me: Union[bool,int], timestamp: int, time: str, key_id: int):
|
def __init__(self, from_me: Union[bool,int], timestamp: int, time: Union[int,str], key_id: int):
|
||||||
self.from_me = bool(from_me)
|
self.from_me = bool(from_me)
|
||||||
self.timestamp = timestamp / 1000 if timestamp > 9999999999 else timestamp
|
self.timestamp = timestamp / 1000 if timestamp > 9999999999 else timestamp
|
||||||
self.time = datetime.fromtimestamp(time/1000).strftime("%H:%M")
|
if isinstance(time, int):
|
||||||
|
self.time = datetime.fromtimestamp(time/1000).strftime("%H:%M")
|
||||||
|
elif isinstance(time, str):
|
||||||
|
self.time = time
|
||||||
self.media = False
|
self.media = False
|
||||||
self.key_id = key_id
|
self.key_id = key_id
|
||||||
self.meta = False
|
self.meta = False
|
||||||
|
|||||||
81
Whatsapp_Chat_Exporter/extract_exported.py
Normal file
81
Whatsapp_Chat_Exporter/extract_exported.py
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
from datetime import datetime
|
||||||
|
from mimetypes import MimeTypes
|
||||||
|
import os
|
||||||
|
from Whatsapp_Chat_Exporter.data_model import ChatStore, Message
|
||||||
|
|
||||||
|
|
||||||
|
def messages(path, data, assume_first_as_me=False):
|
||||||
|
"""Extracts messages from the exported file"""
|
||||||
|
with open(path, "r", encoding="utf8") as file:
|
||||||
|
you = ""
|
||||||
|
data["chat"] = ChatStore()
|
||||||
|
total_row_number = len(file.readlines())
|
||||||
|
i = 0
|
||||||
|
file.seek(0)
|
||||||
|
for index, line in enumerate(file):
|
||||||
|
if len(line.split(" - ")) > 1:
|
||||||
|
time = line.split(" - ")[0]
|
||||||
|
if ":" not in line.split(time)[1]:
|
||||||
|
msg.data = line.split(time)[1][3:]
|
||||||
|
msg.meta = True
|
||||||
|
else:
|
||||||
|
name = line.split(time)[1].split(":")[0]
|
||||||
|
message = line.split(time)[1].split(name + ":")[1].strip()
|
||||||
|
name = name[3:]
|
||||||
|
if you == "":
|
||||||
|
if data["chat"].name is None:
|
||||||
|
if not assume_first_as_me:
|
||||||
|
while True:
|
||||||
|
ans = input(f"Is '{name}' you? (Y/N)").lower()
|
||||||
|
if ans == "y":
|
||||||
|
you = name
|
||||||
|
break
|
||||||
|
elif ans == "n":
|
||||||
|
data["chat"].name = name
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
you = name
|
||||||
|
else:
|
||||||
|
if name != data["chat"].name:
|
||||||
|
you = name
|
||||||
|
if data["chat"].name is None and you != "":
|
||||||
|
if name != you:
|
||||||
|
data["chat"].name = name
|
||||||
|
msg = Message(
|
||||||
|
you == name,
|
||||||
|
datetime.strptime(time, "%d/%m/%Y, %H:%M").timestamp(),
|
||||||
|
time.split(", ")[1].strip(),
|
||||||
|
index
|
||||||
|
)
|
||||||
|
if "<Media omitted>" in message:
|
||||||
|
msg.data = "The media is missing"
|
||||||
|
msg.mime = "media"
|
||||||
|
msg.meta = True
|
||||||
|
elif "(file attached)" in message:
|
||||||
|
mime = MimeTypes()
|
||||||
|
msg.media = True
|
||||||
|
file_path = os.path.join(os.path.dirname(path), message.split("(file attached)")[0].strip())
|
||||||
|
if os.path.isfile(file_path):
|
||||||
|
msg.data = file_path
|
||||||
|
guess = mime.guess_type(file_path)[0]
|
||||||
|
if guess is not None:
|
||||||
|
msg.mime = guess
|
||||||
|
else:
|
||||||
|
msg.mime = "application/octet-stream"
|
||||||
|
else:
|
||||||
|
msg.data = message
|
||||||
|
data["chat"].add_message(index, msg)
|
||||||
|
else:
|
||||||
|
lookback = index - 1
|
||||||
|
while lookback not in data["chat"].messages:
|
||||||
|
lookback -= 1
|
||||||
|
msg = data["chat"].messages[lookback]
|
||||||
|
if msg.media:
|
||||||
|
msg.caption = line.strip()
|
||||||
|
else:
|
||||||
|
msg.data += "<br>" + line.strip()
|
||||||
|
|
||||||
|
if index % 1000 == 0:
|
||||||
|
print(f"Gathering messages & media...({index}/{total_row_number})", end="\r")
|
||||||
|
print(f"Gathering messages & media...({total_row_number}/{total_row_number})", end="\r")
|
||||||
|
return data
|
||||||
Reference in New Issue
Block a user