renamed to main.py; added json output, select all, color

This commit is contained in:
mrbesen 2019-07-19 13:47:49 +02:00
parent d82f1b2bb2
commit 32b6e41b97
Signed by: MrBesen
GPG Key ID: 596B2350DCD67504
5 changed files with 317 additions and 184 deletions

6
.gitignore vendored
View File

@ -1,4 +1,8 @@
config.ini
*.session
Pipfile.lock
.idea/
.idea/
*.csv
*.json
*.xml
*.sqlite

View File

@ -9,6 +9,7 @@ verify_ssl = true
httplib2 = "*"
cryptg = "*"
telethon = "*"
colorama = "*"
[requires]
python_version = "3.6"

View File

@ -1,183 +0,0 @@
#!/usr/bin/env python3
from telethon import TelegramClient, sync
import configparser, math, datetime
dialogs = [] # list of known chats
me = None # the own entity
client = None
output = None # "csv", "stdout", "yaml", "json"...
def printDialog(interid, d): # debug only
print('{0:3d}| {1:14d} | {2:30} | {3:1}'.format(interid, d.id, d.name, d.pinned))
def getUsernamebyID(userid):
for d in dialogs:
if d.id == userid:
return d.name
return "Unknown" # refresh dialog cache?
class Stat:
name = None
def __init__(self, name):
self.name = name
def addMsg(self, msgnum, msg, chat):
pass
def getValue(self, count):
pass
class Countable(Stat):
acc = 0
mult = 0
min = 1 << 31
max = 0
def parse(self, val):
return str(val)
def getValue(self, count):
return "Min: " + self.parse(self.min) + " Max: " + self.parse(self.max) + " Gesamt: " \
+ self.parse(self.acc) + " Avg: " + self.parse(self.acc / count) + " Geom.Avg: " + self.parse(math.sqrt(self.mult))
def addMsg(self, msgnum, msg, chat):
count = self.count(msgnum, msg, chat)
if count > self.max:
self.max = count
if count < self.min:
self.min = count
self.acc = self.acc + count
self.mult = self.mult + (count * count)
def count(self, msgnum, msg, chat):
pass
class CharCount(Countable):
def __init__(self, name: str = "CharCount"):
super().__init__(name)
def count(self, msgnum, msg, chat):
if msg.message != None:
return len(msg.message)
return 0
class Dist(Countable):
prev = 0
def __init__(self, name: str = "Distanz(s)"):
super().__init__(name)
def count(self, msgnum, msg, chat):
if msg.date != None:
old = self.prev
self.prev = msg.date
if old == 0:
return 0
return int((old - msg.date).total_seconds())
def parse(self, val):
return str(datetime.timedelta(seconds=val))
class DiscreteCount(Stat):
list = {}
def getValue(self, count):
out = ''
# sorted_ = sorted(list.items(), key=lambda kv: kv[1])
for name, count in self.list.items():
if len(out) > 0:
out = out + ", "
out = out + name + ": " + str(count) # TODO: add percantage
return out;
class UserCount(DiscreteCount):
def __init__(self, name: str = "Msg/User"):
super().__init__(name)
def addMsg(self, msgnum, msg, chat):
global me
fromid = msg.from_id
self.list[fromid] = self.list.get(fromid, 0) + 1
def getValue(self, count):
# replace ids with names
newdict = {}
for id, count in self.list.items():
newdict[getUsernamebyID(id)] = count
self.list = newdict
return super().getValue(count)
stats = {
CharCount(),
Dist(),
UserCount()
}
# ==================================
# MAIN program
# read config
try:
config = configparser.ConfigParser()
config.read('config.ini')
api_id = int(config.get('Main', 'api_id'))
api_hash = config.get('Main', 'api_hash')
session_name = config.get('Main', 'user')
except (configparser.NoSectionError, configparser.NoOptionError, ValueError):
print('invalid config.ini')
exit(3)
client = TelegramClient(session_name, api_id, api_hash)
client.start()
me = client.get_me()
print('me.id: ', me.id)
# get dialogs
dialogs = client.get_dialogs()
dialogCount = len(dialogs)
print(dialogCount, ' Chats loaded.')
print(
' Internal ID | Username | pinned\n———+————————————————+————————————————————————————————+———————')
interid = 0
for d in dialogs:
printDialog(interid, d)
interid = interid + 1
get = int(input("Please Enter Chat ID: "))
if get < 0 or get >= dialogCount:
print("Unknown Chat ID!")
exit(1)
selectedDialog = dialogs[get]
print("selected: \"", selectedDialog.name, '"; retriving chat!', sep='')
# chat = client.get_messages(selectedDialog)
chat = client.iter_messages(selectedDialog, limit=None)
# run messure
msgnum = 0
for msg in chat:
if msg != None:
msgnum = msgnum + 1
for stat in stats:
stat.addMsg(msgnum, msg, selectedDialog)
print(msgnum, 'Nachrichten Analysiert')
for stat in stats:
print(stat.name, ': ', stat.getValue(msgnum), sep='')

259
src/main.py Executable file
View File

@ -0,0 +1,259 @@
#!/usr/bin/env python3
from telethon import TelegramClient, sync
from timeit import default_timer as timer
import configparser
import out
import math, datetime
import colorama
from colorama import Fore, Style
class Stat:
name = None
def __init__(self, name):
self.name = name
def addMsg(self, msgnum, msg, chat):
pass
def getValue(self, count):
out = ''
all = self.getAll(count)
if len(all) == 0:
return ''
if len(all) == 1:
return next(iter(all))
for name, val in all.items():
if out:
out = out + ", "
out = out + name + ": " + str(val)
return out
def getAll(self, count):
pass
def parse(self, val):
return str(val)
class Countable(Stat):
acc = 0
mult = 0
min = 1 << 31
max = 0
"""def getValue(self, count):
return "Min: " + self.getMin() + " Max: " + self.getMax() + " Gesamt: " \
+ self.getAcc() + " Avg: " + self.getAvg(count) + " Geom.Avg: " + self.getGAvg()
"""
def getMin(self):
return self.parse(self.min)
def getMax(self):
return self.parse(self.max)
def getAcc(self):
return self.parse(self.acc)
def getAvg(self, count):
return self.parse(int(self.acc / count))
def getGAvg(self):
return self.parse(math.sqrt(self.mult))
def getAll(self, count):
return {'min': self.getMin(), 'max': self.getMax(), 'count': self.getAcc(), 'avg': self.getAvg(count), 'gavg': self.getGAvg()}
def addMsg(self, msgnum, msg, chat):
count = self.count(msgnum, msg, chat)
if count > self.max:
self.max = count
if count < self.min:
self.min = count
self.acc = self.acc + count
self.mult = self.mult + (count * count)
def count(self, msgnum, msg, chat):
pass
class CharCount(Countable):
def __init__(self, name: str = "CharCount"):
super().__init__(name)
def count(self, msgnum, msg, chat):
if msg.message != None:
return len(msg.message)
return 0
class Dist(Countable):
prev = 0
def __init__(self, name: str = "Distanz"):
super().__init__(name)
def count(self, msgnum, msg, chat):
if msg.date != None:
old = self.prev
self.prev = msg.date
if old == 0:
return 0
return int((old - msg.date).total_seconds())
def parse(self, val):
return str(datetime.timedelta(seconds=val))
def getAvg(self, count):
if count < 2:
return -1
return self.parse(int(self.acc / (count-1))) # da hier die abstände zwischen nachrichten betrachtet werden ist es um 1 kleiner
class DiscreteCount(Stat):
list = {}
"""def getValue(self, count):
out = ''
# sorted_ = sorted(list.items(), key=lambda kv: kv[1])
for name, count in self.list.items():
if len(out) > 0:
out = out + ", "
out = out + name + ": " + str(count)
return out;"""
def getAll(self, count):
return self.list
class UserCount(DiscreteCount):
def __init__(self, name: str = "Msg/User"):
super().__init__(name)
def addMsg(self, msgnum, msg, chat):
global me
fromid = msg.from_id
self.list[fromid] = self.list.get(fromid, 0) + 1
def getAll(self, count):
# replace ids with names
newdict = {}
for id, count in self.list.items():
newdict[getUsernamebyID(id)] = count
return newdict
def printDialog(interid, d): # debug only
color = ''
if interid & 1:
color = Fore.LIGHTBLACK_EX
print(color + '{0:3d}| {1:14d} | {2:30} | {3:1}'.format(interid, d.id, d.name, d.pinned) + Fore.RESET)
def getUsernamebyID(userid):
global dialogs
for d in dialogs:
if d.id == userid:
return d.name
return "Unknown" # refresh dialog cache?
def analyseChat(dialog, output):
print(Fore.GREEN + 'Lade kompletten Chat: ' + Fore.RESET, dialog.name, sep='')
# chat = client.get_messages(selectedDialog)
chat = client.iter_messages(dialog, limit=None)
# run messure
msgnum = 0
for msg in chat:
if msg != None:
msgnum = msgnum + 1
for stat in stats:
stat.addMsg(msgnum, msg, dialog)
print(Fore.BLUE, msgnum, Fore.MAGENTA + 'Nachrichten Analysiert' + Fore.RESET)
# write to output
output.print(stats, msgnum, dialog)
# ==================================
# MAIN program
if __name__ == "__main__":
stats = {
CharCount(),
Dist(),
UserCount()
}
outputs = {
"stdout": out.STDOUT(),
"json": out.jsonOut()
# TODO: add csv,xml,yaml maybe sqlite or mysql?
}
# read config
try:
config = configparser.ConfigParser()
config.read('config.ini')
api_id = int(config.get('Main', 'api_id'))
api_hash = config.get('Main', 'api_hash')
session_name = config.get('Main', 'user')
except (configparser.NoSectionError, configparser.NoOptionError, ValueError):
print('invalid config.ini')
exit(3)
client = TelegramClient(session_name, api_id, api_hash)
client.start()
me = client.get_me()
print('me.id: ', me.id)
# get dialogs
dialogs = client.get_dialogs()
dialogCount = len(dialogs)
print(Fore.YELLOW, dialogCount, Fore.GREEN + 'Chats geladen.' + Fore.RESET)
#select output
while True:
uinput = input("Bitte Ausgabemethode wählen: [stdout,json] (stdout): ").strip().lower();
if uinput == "":
uinput = "stdout"
try:
out = outputs[uinput]
break
except KeyError:
pass # try again
outfile = None
if out.needsFilename:
outfile = input("Bitte Dateinamen eingeben: ").strip()
out.open(outfile)
#select chat
print(' ID | Internal ID | Username | pinned\n———+————————————————+————————————————————————————————+———————')
interid = 0
for d in dialogs:
printDialog(interid, d)
interid = interid + 1
uinput = input('Bitte ' + Fore.BLUE + 'chatid' + Fore.RESET + ' oder "' + Fore.BLUE + 'all' + Fore.RESET + '" eingeben: ').strip().lower() # TODO: select more than one or all
start = timer()
if uinput == 'all':
for d in dialogs:
analyseChat(d, out)
else:
get = int(uinput)
if get < 0 or get >= dialogCount: #TODO: detect user by username, name, tg-id
print("unbekannte Chat ID!")
exit(1)
analyseChat(dialogs[get], out)
out.close()
took = datetime.timedelta(seconds=(timer()-start))
took = datetime.timedelta(seconds=int(took.total_seconds()))
print(Fore.GREEN + "Fertig. Benötigte Zeit: " + Fore.YELLOW, took, Style.RESET_ALL)

52
src/out.py Normal file
View File

@ -0,0 +1,52 @@
import json
class Output:
name = None
needsFilename = False
def __init__(self, name):
super().__init__()
self.name = name
def open(self, file: str = None):
pass
def close(self):
pass
def print(self, stats, count, dialog):
pass
class STDOUT(Output):
def __init__(self, name: str = "stdout"):
super().__init__(name)
def print(self, stats, count, dialog):
for stat in stats:
print(stat.name, ': ', stat.getValue(count), sep='')
class jsonOut(Output):
file = None
needsFilename = True
outbuff = {}
def __init__(self, name: str = "json"):
super().__init__(name)
def open(self, file):
if file is None:
file = "out.json"
self.file = open(file, "w")
def close(self):
json.dump(self.outbuff, self.file, indent=4)
self.file.close()
def print(self, stats, count, dialog):
jsonpre = {"count": count}
for stat in stats:
jsonpre[stat.name] = stat.getAll(count)
self.outbuff[dialog.id] = jsonpre