Hello world

This commit is contained in:
Fierelier 2020-11-17 16:59:42 +01:00
commit 424cf77ced
3 changed files with 617 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
logins/

20
default.ini Normal file
View File

@ -0,0 +1,20 @@
[performance]
; How many messages should be retrieved upon opening a conversation?
historyFetch = 20
; How many messages can be collectively displayed in a conversation?
maxMessagesListed = 100
[font]
; Font face. Only changes some elements.
type = Tahoma
; Font size in px. Only changes some elements, unsupported.
size = 8
[color]
; Color used to highlight user in chat log
userHighlight = #ff0000
; Conversation/friend-list colors
userOnline = #00ff00
userAway = #ffff00
userBusy = #ff0000
userOffline = #999999

596
discord-but-fast.py Normal file
View File

@ -0,0 +1,596 @@
#!/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 tkinter
import tkinter.ttk
import threading
import logging
import configparser
distro = "Discord but fast"
version = (0,0,0)
versionString = ".".join(map(str,version))
pathLogins = p(sp,"logins")
initialized = False
connected = False
def getTitle(text = False):
title = ""
if text == False:
title = distro + " " + versionString
else:
title = text + " - " + distro + " " + versionString
if initialized == False: return title
if connected == True: return title
return "[Offline] " +title
def fetchLogins():
logins = []
for root,dirs,files in os.walk(pathLogins):
for file in dirs:
ffile = p(root,file)
lfile = ffile.replace(pathLogins + os.sep,"")
logins.append(lfile)
return logins
def filterText(text):
newtext = ""
for char in text:
if not ord(char) in range(65536):
char = "?"
newtext += char
return newtext
def taggedText(textObj,text):
textObj.delete("1.0","end")
lineIndex = 1
columnIndex = 0
index = 0
length = len(text)
while index < length:
tags = text[index]
part = text[index + 1]
newLineIndex = lineIndex + part.count("\n")
if newLineIndex != lineIndex:
newColumnIndex = len(part.rsplit("\n",1))
else:
newColumnIndex = len(part)
textObj.insert(tkinter.END,part)
for tag in tags.split(";"):
textObj.tag_add(tag,str(lineIndex)+ "." +str(columnIndex),str(newLineIndex)+ "." +str(newColumnIndex))
lineIndex = newLineIndex
index = index + 2
def getChannelDisplayName(channel):
if type(channel) == discord.DMChannel:
return filterText(channel.recipient.name) + "#" +channel.recipient.discriminator
elif type(channel) == discord.GroupChannel:
if channel.name:
return "Group: " +filterText(channel.name)
return "Group: [No name]"
class guiLoginChooser:
def __init__(self):
self.window = tkinter.Tk()
self.title = "Choose login"
self.width = 320
self.height = 82
self.window.geometry(str(self.width) + "x" + str(self.height))
self.window.minsize(220,82)
self.window.title(getTitle(self.title))
self.createElements()
self.window.bind("<Configure>", self.onResize)
self.window.protocol("WM_DELETE_WINDOW", self.onClose)
def createElements(self):
self.label = tkinter.Label(self.window, text="Select a login:", font=(defaultFont,defaultFontSize))
self.choices = fetchLogins()
self.login = ""
self.dropdownLoginChoice = tkinter.StringVar(self.window)
self.dropdownLogin = tkinter.OptionMenu(self.window, self.dropdownLoginChoice, *self.choices)
self.buttonNew = tkinter.Button(self.window, text="New", command=self.window.quit, font=(defaultFont,defaultFontSize))
self.buttonLogin = tkinter.Button(self.window, text="Login", command=self.confirm, font=(defaultFont,defaultFontSize))
self.resizeElements()
def resizeElements(self):
self.label.place(
x = 5,
y = 5,
width = self.width - 10,
height = 12
)
self.dropdownLogin.place(
x = 40,
y = 20,
width = self.width - 80,
height = 24
)
self.buttonNew.place(
x = 5,
y = self.height - 24 - 5,
width = 64,
height = 24
)
self.buttonLogin.place(
x = self.width - 64 - 5,
y = self.height - 24 - 5,
width = 64,
height = 24
)
def onResize(self,event):
if event.widget != self.window: return
if event.width == self.width and event.height == self.height: return
self.width = event.width
self.height = event.height
self.resizeElements()
def onClose(self):
self.window.destroy()
def confirm(self):
if self.dropdownLoginChoice.get() == "":
import tkinter.messagebox as messagebox
messagebox.showinfo(title=getTitle("Info"), message='Please choose a login from the dropdown menu, or create one by pressing "New"!')
return
self.login = self.dropdownLoginChoice.get()
self.window.destroy()
class guiLoginProgress:
def __init__(self):
self.window = tkinter.Tk()
self.title = "Logging in..."
self.width = 320
self.height = 240
self.window.geometry(str(self.width) + "x" + str(self.height))
self.window.title(getTitle(self.title))
self.createElements()
self.window.bind("<Configure>", self.onResize)
self.window.protocol("WM_DELETE_WINDOW", self.onClose)
def createElements(self):
self.log = tkinter.Text(self.window, font=(defaultFont,defaultFontSize))
self.logScrollbar = tkinter.ttk.Scrollbar(self.window,command=self.log.yview)
self.log.config(state=tkinter.DISABLED)
self.log.bind("<1>", lambda event: self.log.focus_set())
self.log["yscrollcommand"] = self.logScrollbar.set
self.resizeElements()
def resizeElements(self):
self.log.place(
x = 0,
y = 0,
width = self.width - 20,
height = self.height
)
self.logScrollbar.place(
x = self.width - 20,
y = 0,
width = 20,
height = self.height
)
def addToLog(self,text):
self.log.config(state=tkinter.NORMAL)
self.log.insert(tkinter.END,text)
self.log.config(state=tkinter.DISABLED)
self.log.see(tkinter.END)
self.window.update()
def onResize(self,event):
if event.widget != self.window: return
if event.width == self.width and event.height == self.height: return
self.width = event.width
self.height = event.height
self.resizeElements()
def onClose(self):
self.window.destroy()
class guiConversationList:
def __init__(self):
self.type = "conversationList"
self.closed = False
self.window = tkinter.Tk()
self.title = "Conversations"
self.width = 200
self.height = 350
self.window.geometry(str(self.width) + "x" + str(self.height))
self.window.title(getTitle(self.title))
self.createElements()
self.window.bind("<Configure>", self.onResize)
self.window.protocol("WM_DELETE_WINDOW", self.onClose)
def createElements(self):
self.menuConv = tkinter.Menu(self.window,tearoff=0,font=(defaultFont,defaultFontSize))
self.menuConv.add_command(label="New")
self.menuApp = tkinter.Menu(self.window,tearoff=0,font=(defaultFont,defaultFontSize))
self.menuApp.add_command(label="New instance")
self.menuApp.add_command(label="Exit")
self.menubar = tkinter.Menu(self.window,font=(defaultFont,defaultFontSize))
self.menubar.add_cascade(label="App", menu=self.menuApp)
self.menubar.add_cascade(label="Conversation", menu=self.menuConv)
self.window.config(menu=self.menubar)
self.pfp = tkinter.Canvas(self.window,bg="#ffffff")
self.name = tkinter.Label(self.window,text=filterText(client.user.name + "#" +client.user.discriminator),anchor="w",font=(defaultFont,defaultFontSize,"bold"))
self.statusImg = tkinter.Canvas(self.window,bg="#00ff00")
self.status = tkinter.Label(self.window,text="A status",anchor="w",font=(defaultFont,defaultFontSize))
self.tablist = tkinter.ttk.Notebook(self.window)
self.tabConv = tkinter.ttk.Frame(self.tablist)
self.convs = []
self.tabFriends = tkinter.ttk.Frame(self.tablist)
self.tablist.add(self.tabConv,text="Conversations")
self.tablist.add(self.tabFriends,text="Friends")
self.listConv = tkinter.Listbox(self.tabConv,font=(defaultFont,defaultFontSize))
self.listConvScrollbar = tkinter.ttk.Scrollbar(self.tabConv,command=self.listConv.yview)
self.listConv["yscrollcommand"] = self.listConvScrollbar.set
self.listConv.bind("<Double-1>",self.listConvSelect)
self.listConv.bind("<Return>",self.listConvSelect)
self.resizeElements()
def listConvSelect(self,evt):
index = int(evt.widget.curselection()[0])
openGuis.append(guiConversation(self.convs[index]))
def updateConvs(self,channels):
size = self.listConv.size()
self.listConv.delete(0,size - 1)
listIndex = 1
self.convs = []
for channel in channels:
self.listConv.insert(listIndex," " +getChannelDisplayName(channel)+ " ")
self.convs.append(channel)
listIndex = listIndex + 1
def resizeElements(self):
pfps = 50
self.pfp.place(
x = 5,
y = 5,
width = pfps,
height = pfps
)
self.name.place(
x = pfps + 10,
y = 10,
width = self.width - pfps - 10,
height = 16
)
self.statusImg.place(
x = pfps + 10,
y = 10 + 16,
width = 16,
height = 16
)
self.status.place(
x = pfps + 10 + 16 + 2,
y = 10 + 16,
width = self.width - pfps - 10 - 16 - 2,
height = 16
)
self.tablist.place(
x = 0,
y = pfps + 10,
width = self.width,
height = self.height - pfps - 10
)
self.listConv.place(
x = 0,
y = 0,
relwidth = 1.0,
width = -20,
relheight = 1.0
)
self.listConvScrollbar.place(
x = -20,
y = 0,
relx = 1.0,
width = 20,
relheight = 1.0
)
def onResize(self,event):
if event.widget != self.window: return
if event.width == self.width and event.height == self.height: return
self.width = event.width
self.height = event.height
self.resizeElements()
def onClose(self):
self.closeGui()
def closeGui(self):
self.closed = True
self.window.destroy()
class guiConversation:
def __init__(self,channel):
self.type = "conversation"
self.channel = channel
self.closed = False
self.readied = False
self.pendingMessages = []
self.messages = []
self.window = tkinter.Tk()
self.title = getChannelDisplayName(channel)
self.width = 380
self.height = 360
self.window.geometry(str(self.width) + "x" + str(self.height))
self.window.title(getTitle(self.title))
self.createElements()
self.window.bind("<Configure>", self.onResize)
self.window.protocol("WM_DELETE_WINDOW", self.onClose)
def createElements(self):
self.menuConv = tkinter.Menu(self.window,tearoff=0,font=(defaultFont,defaultFontSize))
self.menuConv.add_command(label="Add member")
self.menuConv.add_command(label="Remove member")
self.menuConv.add_command(label="Leave")
self.menuConv.add_separator()
self.menuConv.add_command(label="Save")
self.menuConv.add_separator()
self.menuConv.add_command(label="Close")
self.menubar = tkinter.Menu(self.window)
self.menubar.add_cascade(label="Conversation", menu=self.menuConv)
self.window.config(menu=self.menubar)
self.messagelog = tkinter.Text(self.window,wrap=tkinter.WORD,font=(defaultFont,defaultFontSize))
self.messagelog.insert(tkinter.END,"Fetching messages...")
self.messagelogScrollbar = tkinter.ttk.Scrollbar(self.window,command=self.messagelog.yview)
self.messagelog["yscrollcommand"] = self.messagelogScrollbar.set
self.messagelog.config(state=tkinter.DISABLED)
self.messagelog.bind("<1>", lambda event: self.messagelog.focus_set())
self.messagelog.tag_config("user",foreground=config["color"]["userHighlight"],font=(defaultFont,defaultFontSize,"bold"))
self.messagebox = tkinter.Text(self.window,wrap=tkinter.WORD,font=(defaultFont,defaultFontSize))
self.messageboxScrollbar = tkinter.ttk.Scrollbar(self.window,command=self.messagebox.yview)
self.messagebox["yscrollcommand"] = self.messageboxScrollbar.set
self.messagebox.bind("<Return>",self.sendMessagebox)
self.resizeElements()
def resizeElements(self):
self.messagelog.place(
x = 0,
y = 0,
width = self.width - 20,
height = self.height - 48
)
self.messagelogScrollbar.place(
x = self.width - 20,
y = 0,
width = 20,
height = self.height - 48
)
self.messagebox.place(
x = 0,
y = self.height - 48,
width = self.width - 20,
height = 48
)
self.messageboxScrollbar.place(
x = self.width - 20,
y = self.height - 48,
width = 20,
height = 48
)
def onResize(self,event):
if event.widget != self.window: return
if event.width == self.width and event.height == self.height: return
self.width = event.width
self.height = event.height
self.resizeElements()
def onClose(self):
self.closeGui()
def closeGui(self):
self.closed = True
self.window.destroy()
def addMessage(self,message):
if len(self.messages) > int(config["performance"]["maxMessagesListed"]):
del self.messages[0]
self.messages.append(message)
def fillMessageLog(self):
taggedTextList = []
for message in self.messages:
if type(message) == discord.Message:
taggedTextList += [
"","\n",
"user",filterText(message.author.name) + ":",
""," " + filterText(message.content)
]
for attachment in message.attachments:
taggedTextList += [
"","\n> Attach: " +attachment.url
]
else:
taggedTextList += message
self.messagelog.config(state=tkinter.NORMAL)
taggedText(self.messagelog,taggedTextList)
self.messagelog.config(state=tkinter.DISABLED)
self.messagelog.see("end")
def sendMessagebox(self,evt=False):
messageText = self.messagebox.get("1.0","end")
self.pendingMessages.append(messageText)
self.messagebox.delete("1.0","end")
def main():
global config
config = configparser.ConfigParser()
config.read(p(sp,"default.ini"))
global defaultFont
global defaultFontSize
defaultFont = config["font"]["type"]
defaultFontSize = config["font"]["size"]
loginChooser = guiLoginChooser()
loginChooser.window.mainloop()
global login
login = loginChooser.login
if login == "": return
token = False
global iLoginProgress
iLoginProgress = guiLoginProgress()
iLoginProgress.addToLog("Fetching token...")
with open(p(pathLogins,login,"token"),"r") as fileh:
token = fileh.read()
iLoginProgress.addToLog("\nLoading discord.py...")
global discord
import discord
global tasks
from discord.ext import tasks
logging.basicConfig(level=logging.INFO)
iLoginProgress.addToLog("\nConnecting...")
global client
client = discord.Client()
@client.event
async def on_ready():
print("on_ready")
global initialized
if initialized == False:
iLoginProgress.window.destroy()
global openGuis
openGuis = []
openGuis.append(guiConversationList())
guiLoop.start()
conversationUpdate.start()
initialized = True
global connected
connected = True
for gui in openGuis:
if gui.closed == True: continue
gui.window.title(getTitle(gui.title))
await client.change_presence(status=discord.Status.dnd, activity=discord.CustomActivity("working on discord but fast"))
@client.event
async def on_resumed():
print("on_resumed")
global connected
connected = True
for gui in openGuis:
if gui.closed == True: continue
gui.window.title(getTitle(gui.title))
@client.event
async def on_disconnect():
print("on_disconnect")
global connected
connected = False
for gui in openGuis:
if gui.closed == True: continue
gui.window.title(getTitle(gui.title))
@client.event
async def on_message(message):
for gui in openGuis:
if gui.closed == True: continue
if gui.type == "conversation":
if message.channel != gui.channel: continue
gui.addMessage(message)
gui.fillMessageLog()
@tasks.loop(seconds=0.033)
async def guiLoop():
length = len(openGuis)
index = 0
while index < length:
gui = openGuis[index]
if gui.closed == True:
del openGuis[index]
length = length - 1
continue
if gui.type == "conversation":
if gui.readied == False:
try:
messages = await gui.channel.history(limit=int(config["performance"]["historyFetch"])).flatten()
for message in reversed(messages):
gui.addMessage(message)
except:
gui.addMessage(["","Fetching messages failed."])
gui.fillMessageLog()
gui.readied = True
while len(gui.pendingMessages) > 0:
message = ""
try:
message = gui.pendingMessages.pop(0)
await gui.channel.send(message)
except:
print("error sending message")
gui.window.update()
index = index + 1
@tasks.loop(seconds=1.0)
async def conversationUpdate():
channels = client.private_channels
for gui in openGuis:
if gui.type != "conversationList": continue
if gui.closed == True: continue
if channels == gui.convs: continue
gui.updateConvs(channels)
client.run(token,bot = False)
if __name__ == "__main__":
main()