引言
CrossChat(十字街)是一个线上匿名群聊平台,类似Hack.Chat
。
现在通过websocket简单地构建一个cc的客户端。有以下几点注意:
-
只提供代码和基础思路,不提供代码功能详解
-
仅作为学习使用,不具备应用功能
声明
本项目属于作者原创。借鉴了GitHub/TinUI上的tuxml.py
,翻版必究,但可以自行添加功能代码。
本项目使用的TinUI为我开源并维护在GitHub上的主文件——TinUI.py。当然,使用PYPI中下载安装的tinui也可以。
文件结构
-
crosschat.py - 参考hackchat.py
-
crosschat客户端.pyw - 主文件
-
loadin.xml - 聊天区登录界面
-
main.xml - 主页面
核心代码
服务端连接
参考pypi上的hackchat包,建立websocket连接。crosschat.py。
import json
import threading
import time
import websocket
class CrossChat:
"""A library to connect to https://cross.chat/
"""
def __init__(self, nick, channel="programming"):
"""Connects to a channel on https://cross.chat.
Keyword arguments:
nick -- <str>; the nickname to use upon joining the channel
channel -- <str>; the channel to connect to on https://hack.chat/
"""
self.nick = nick
self.channel = channel
self.online_users = []
self.on_message = []
self.on_join = []
self.on_leave = []
self.ws = websocket.create_connection("wss://ws.crosst.chat:35197/")
self._send_packet({"cmd": "join", "channel": channel, "nick": nick})
threading.Thread(target = self._ping_thread).start()
def send_message(self, msg):
"""Sends a message on the channel."""
self._send_packet({"cmd": "chat", "text": msg})
def _send_packet(self, packet):
"""Sends <packet> (<dict>) to https://cross.chat."""
encoded = json.dumps(packet)
self.ws.send(encoded)
def run(self):
"""Sends data to the callback functions."""
while True:
result = json.loads(self.ws.recv())
if result["cmd"] == "chat" and not result["nick"] == self.nick:
for handler in list(self.on_message):
handler(self, result["text"], result["nick"])
elif result["cmd"] == "onlineAdd":
self.online_users.append(result["nick"])
for handler in list(self.on_join):
handler(self, result["nick"])
elif result["cmd"] == "onlineRemove":
self.online_users.remove(result["nick"])
for handler in list(self.on_leave):
handler(self, result["nick"])
elif result["cmd"] == "onlineSet":
for nick in result["nicks"]:
self.online_users.append(nick)
def _ping_thread(self):
"""Retains the websocket connection."""
while self.ws.connected:
self._send_packet({"cmd": "ping"})
time.sleep(60)
登录界面
其实很简单,就是输入房间号和昵称。loadin.xml。
<!--TinUIXml编辑器-->
<tinui>
<line y="15">
<line>
<paragraph text="chat room id"></paragraph>
</line>
<line>
<paragraph text="nick name"></paragraph>
</line>
<back></back>
<line>
<entry width="210">roomide</entry>
</line>
<line>
<entry width="210">nicknamee</entry>
</line>
</line>
<line x="60">
<paragraph text=" "></paragraph>
<button2 text="清空" command='self.funcs["cleanover"]'></button2>
<paragraph text=" "></paragraph>
<button2 text="进入" command='self.funcs["login"]'></button2>
</line>
</tinui>
主页面
main.xml。
<!--TinUIXml UI界面布局-->
<!--TinUIXml UI界面布局-->
<tinui>
<line>
<line>
<ui width="650" height="460" scrollbar="True">content</ui>
</line>
<line>
<textbox width="650" height="180" scrollbar="True">textbox</textbox>
</line>
<line>
<paragraph text=" " width="1000"></paragraph>
<button2 text="发送文本(Ctrl+Enter)" command='self.funcs["sendmsg"]'></button2>
</line>
<back></back>
<line>
<button2 text="使用须知📝" command='self.funcs["aboutuse"]'></button2>
<button2 text="关于应用🔎" command='self.funcs["aboutccpy"]'></button2>
<button2 text="注意事项❗" command='self.funcs["warnccpy"]'></button2>
</line>
<line>
<link text="基于hack.chat开发" url="https://hack.chat/"></link>
</line>
<line>
<ui width="360" height="610">aboutui</ui>
</line>
</line>
</tinui>
主要的功能UI部分在左边,右边只是给出了可能的附加功能,因此在接下来的主文件中实际上是没有相应的功能代码片段的。
主文件
from tinui import *
from tkinter import Tk
import crosschat
import threading
def endy():
bbox=content.bbox('all')
if bbox==None:
return 0
else:
return bbox[-1]
def addhere(t):
content.add_paragraph((645,endy()+5),fg='#b48ead',text=nickname,anchor='ne')
content.add_paragraph((645,endy()+1),text=t,anchor='ne')
def addthere(t,s):
content.add_paragraph((5,endy()+5),fg='#8fa1b3',text=s,anchor='nw')
content.add_paragraph((5,endy()+1),text=t,anchor='nw')
def addinfo(t):
...
def sendmsg(*e):#
context=textbox.get(1.0,'end')
addhere(context)
ccc.send_message(context)
textbox.delete(1.0,'end')
def aboutuse(*e):#
...
def aboutccpy(*e):#
...
def warnccpy(*e):#
...
def message_got(chat, message, sender):
addthere(message,sender)
root=Tk()
root.geometry('1100x700+5+5')
root.title('CrossChat客户端')
u=BasicTinUI(root)# main ui
u.pack(fill='both',expand=True)
x=TinUIXml(u)
#in
x.funcs["sendmsg"] = sendmsg
x.funcs["aboutuse"] = aboutuse
x.funcs["aboutccpy"] = aboutccpy
x.funcs["warnccpy"] = warnccpy
#during
with open('main.xml',mode='r',encoding='utf-8') as f:
xml=f.read()
x.loadxml(xml)
#out
#out
content = x.tags["content"][0]#ui - main content chat
textbox = x.tags["textbox"][0]#textbox - text to chat
aboutui = x.tags["aboutui"][0]#ui - about view of ccpy
u.pack_forget()
#-----
logu=BasicTinUI(root,width=400,height=300)# log ui
logu.pack()
lx=TinUIXml(logu)
#in
def cleanover(*e):#
roomide.delete(0,'end')
nicknamee.delete(0,'end')
def login(*e):#
global ccc,nickname
logu.pack_forget()
roomid=roomide.get()
nickname=nicknamee.get()
ccc=crosschat.CrossChat(nickname,roomid)
ccc.on_message += [message_got]
u.pack(fill='both',expand=True)
threading.Thread(target = ccc.run).start()
lx.funcs["cleanover"] = cleanover
lx.funcs["login"] = login
with open('loadin.xml',mode='r',encoding='utf-8') as f:
xml=f.read()
lx.loadxml(xml)
#out
roomide = lx.tags["roomide"][0]#entry
nicknamee = lx.tags["nicknamee"][0]#entry
root.mainloop()
结语
现在已经完成了一个简单的CC客户端,其它功能可以通过CC提供的API实现。
🔆tkinter创新🔆