Hello world
This commit is contained in:
commit
424cf77ced
|
@ -0,0 +1 @@
|
|||
logins/
|
|
@ -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
|
|
@ -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()
|
Loading…
Reference in New Issue