222 lines
6.1 KiB
Python
222 lines
6.1 KiB
Python
#!/usr/bin/env python3
|
|
import sys
|
|
|
|
oldexcepthook = sys.excepthook
|
|
def newexcepthook(type,value,traceback):
|
|
oldexcepthook(type,value,traceback)
|
|
#input("Press ENTER to quit.")
|
|
sys.excepthook = newexcepthook
|
|
|
|
import os
|
|
p = os.path.join
|
|
pUp = os.path.dirname
|
|
s = False
|
|
if getattr(sys, 'frozen', False) and hasattr(sys, '_MEIPASS'):
|
|
s = os.path.realpath(sys.executable)
|
|
else:
|
|
s = os.path.realpath(__file__)
|
|
sp = pUp(s)
|
|
|
|
# script start
|
|
import configparser
|
|
import threading
|
|
import queue
|
|
import datetime
|
|
import json
|
|
|
|
import logging
|
|
#logging.basicConfig(level=logging.INFO)
|
|
|
|
import asyncio
|
|
import discord
|
|
from discord.ext import tasks
|
|
|
|
def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs)
|
|
|
|
userFolder = p(sp,"userdata")
|
|
inQueue = []
|
|
inQueueLock = threading.Lock()
|
|
outQueue = queue.Queue()
|
|
fileLock = threading.Lock()
|
|
thread = False
|
|
fetchMessageLock = threading.Lock()
|
|
|
|
def datetimeToString(dt):
|
|
return json.dumps([dt.year,dt.month,dt.day,dt.hour,dt.minute,dt.second,dt.microsecond])
|
|
|
|
def datetimeFromString(js):
|
|
ls = json.loads(js)
|
|
return datetime.datetime(*ls)
|
|
|
|
def stringToBool(st):
|
|
if st.lower() in ["0","false","no"]:
|
|
return False
|
|
|
|
if st.lower() in ["1","true","yes"]:
|
|
return True
|
|
|
|
raise Exception("syntax","could not convert " +str(st)+ " to bool")
|
|
|
|
def readConfig():
|
|
config = configparser.ConfigParser()
|
|
config.read(s.rsplit(".",1)[0] + ".ini")
|
|
|
|
global cTrackMessages
|
|
cTrackMessages = stringToBool(config["lostMessageTracker"]["trackMessages"])
|
|
|
|
global cAcquireLostMessagesOnReconnect
|
|
cAcquireLostMessagesOnReconnect = stringToBool(config["lostMessageTracker"]["acquireAllOnReconnect"])
|
|
|
|
global cObserveServerChannels
|
|
cObserveServerChannels = stringToBool(config["default"]["observeServerChannels"])
|
|
|
|
global cObserveGroupChannels
|
|
cObserveGroupChannels = stringToBool(config["default"]["observeGroupChannels"])
|
|
|
|
global cObserveDirectMessageChannels
|
|
cObserveDirectMessageChannels = stringToBool(config["default"]["observeDirectMessageChannels"])
|
|
readConfig()
|
|
|
|
def shouldIgnoreChannel(channel):
|
|
if type(channel) == discord.TextChannel:
|
|
if cObserveServerChannels: return False
|
|
return True
|
|
|
|
if type(channel) == discord.GroupChannel:
|
|
if cObserveGroupChannels: return False
|
|
return True
|
|
|
|
if type(channel) == discord.DMChannel:
|
|
if cObserveDirectMessageChannels: return False
|
|
return True
|
|
|
|
return True
|
|
|
|
class discordClient(threading.Thread):
|
|
self = False
|
|
def __init__(self,token):
|
|
threading.Thread.__init__(self)
|
|
self.token = token
|
|
self.loop = asyncio.new_event_loop()
|
|
asyncio.set_event_loop(self.loop)
|
|
self.client = discord.Client()
|
|
self.defineEvents()
|
|
|
|
def defineEvents(self):
|
|
@self.client.event
|
|
async def on_ready(*args,**kwargs): await self.on_ready(*args,**kwargs)
|
|
@self.client.event
|
|
async def on_resumed(*args,**kwargs): await self.on_resumed(*args,**kwargs)
|
|
@self.client.event
|
|
async def on_message(*args,**kwargs): await self.on_message(*args,**kwargs)
|
|
|
|
def getChannelFolder(self,channel):
|
|
return p(self.clientFolder,"channels",str(channel.id))
|
|
|
|
def getChannelMessageIdFile(self,channel):
|
|
return p(self.getChannelFolder(channel),"messageIDs.txt")
|
|
|
|
def readChannelMessageIdFile(self,channel):
|
|
cfile = self.getChannelMessageIdFile(channel)
|
|
if not os.path.isfile(cfile): return []
|
|
data = None
|
|
with open(cfile,"r") as fhandler:
|
|
data = fhandler.read()
|
|
data = data.split("\n")
|
|
data.pop(-1)
|
|
return data
|
|
|
|
def addToChannelMessageIdFile(self,message):
|
|
cfile = self.getChannelMessageIdFile(message.channel)
|
|
if not os.path.isfile(cfile):
|
|
cfolder = self.getChannelFolder(message.channel)
|
|
if not os.path.isdir(cfolder): os.makedirs(cfolder)
|
|
with open(cfile,"w") as fhandler:
|
|
pass
|
|
|
|
with open(cfile,"a") as fhandler:
|
|
fhandler.write(str(message.id) + "\n")
|
|
|
|
def trackMessage(self,message):
|
|
with fileLock:
|
|
messageIDs = self.readChannelMessageIdFile(message.channel)
|
|
if not message.id in messageIDs:
|
|
self.addToChannelMessageIdFile(message)
|
|
return True
|
|
return False
|
|
|
|
def getTrackedChannels(self):
|
|
for guild in self.client.guilds:
|
|
for channel in guild.channels:
|
|
if not shouldIgnoreChannel(channel):
|
|
yield channel
|
|
|
|
for channel in self.client.private_channels:
|
|
if not shouldIgnoreChannel(channel):
|
|
yield channel
|
|
|
|
async def acquireAllLostMessages(self):
|
|
eprint("Acquiring lost messages...")
|
|
with fetchMessageLock:
|
|
for channel in self.getTrackedChannels():
|
|
messageIDs = False
|
|
with fileLock:
|
|
messageIDs = self.readChannelMessageIdFile(channel)
|
|
|
|
messages = []
|
|
lim = 999999
|
|
if messageIDs == []: lim = 100
|
|
async for message in channel.history(limit=lim):
|
|
if str(message.id) in messageIDs: break
|
|
messages.append(message)
|
|
|
|
messages.reverse()
|
|
|
|
messagesFiltered = []
|
|
for message in messages:
|
|
if not str(message.id) in messageIDs:
|
|
messagesFiltered.append(message)
|
|
|
|
with fileLock:
|
|
messageIDs = self.readChannelMessageIdFile(channel)
|
|
for message in messagesFiltered:
|
|
if not str(message.id) in messageIDs:
|
|
self.addToChannelMessageIdFile(message)
|
|
await self.on_message(message, tracked = True)
|
|
eprint("Done acquiring lost messages.")
|
|
|
|
def run(self):
|
|
self.discordTask = self.loop.create_task(self.client.start(self.token,bot = False))
|
|
self.loop.run_forever()
|
|
|
|
async def on_ready(self):
|
|
eprint("on_ready")
|
|
self.clientFolder = p(userFolder,str(self.client.user.id))
|
|
if cAcquireLostMessagesOnReconnect:
|
|
await self.acquireAllLostMessages()
|
|
|
|
async def on_resumed(self):
|
|
eprint("on_resumed")
|
|
#if cAcquireLostMessagesOnReconnect:
|
|
# await self.acquireAllLostMessages()
|
|
|
|
async def on_message(self,message,tracked = False):
|
|
if shouldIgnoreChannel(message.channel): return
|
|
if tracked == False:
|
|
with fetchMessageLock, fileLock:
|
|
messageIDs = self.readChannelMessageIdFile(message.channel)
|
|
if not str(message.id) in messageIDs:
|
|
self.addToChannelMessageIdFile(message)
|
|
|
|
outQueue.put(["message",message,tracked])
|
|
|
|
async def stop(self):
|
|
eprint("Waiting for client to close...")
|
|
await self.client.close()
|
|
eprint("Stopping loop.")
|
|
self.loop.stop()
|
|
|
|
def run(token):
|
|
global thread
|
|
thread = discordClient(token)
|
|
thread.start() |