Skip to content
Open

v4.9 #82

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 60 additions & 58 deletions LTS.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@
"""
# TouchFish 协议文档

本协议文档版本:v2.5.0
本协议文档版本:v2.6.0

本协议分为三个部分:`Gate`,`Chat`,`Server`。
本协议分为三个部分:`Gate`,`Chat`,`Misc`。

协议均使用 NDJSON(JSON 格式,相邻两个 JSON 以换行符分隔)格式进行发送。

---

# 协议更新日志

- Protocol v2.6.0 (TouchFish v4.9.0)
- 恢复 v2.3.0 的字段更改
- Protocol v2.5.0 (TouchFish v4.8.0)
- 撤销 v2.3.0 的字段更改
- Protocol v2.4.0 (TouchFish v4.7.0)
Expand Down Expand Up @@ -215,29 +217,29 @@

---

# 3 Server
# 3 Misc

这个部分是关于程序运行情况的协议内容。

## 3.1 Start

`{ type: "SERVER.START", time: time, stamp: number, server_version: string, config: JSON }`
`{ type: "MISC.START", time: time, stamp: number, version: string, config: JSON }`

程序将启动时的启动参数写入日志。

- `type`: `"SERVER.START"`
- `type`: `"MISC.START"`
- `time`: 同上。
- `stamp`: 用于指定文件保存路径,取启动时的 UNIX 时间戳乘 `10 ** 6` 后向下取整。
- `server_version`: 字符串,表示服务端程序版本。(下同)
- `version`: 字符串,表示服务端程序版本。(下同)
- `config`: JSON 对象,表示启动参数。(具体格式详见代码,下同)

## 3.2 Data

`{ type: "SERVER.DATA", server_version: string, uid: number, config: JSON, users: [JSON, ...], chat_history: [JSON, ...] }`
`{ type: "MISC.DATA", server_version: string, uid: number, config: JSON, users: [JSON, ...], chat_history: [JSON, ...] }`

用于向新连接的客户端提供完整上下文。

- `type`: `"SERVER.DATA"`
- `type`: `"MISC.DATA"`
- `server_version`: 同上。
- `uid`: 表示服务端分配给该用户的用户 ID。
- `config`: 同上。
Expand All @@ -253,83 +255,83 @@

## 3.3 Command

`{ type: "SERVER.COMMAND", time: time, command: string }`
`{ type: "MISC.COMMAND", time: time, command: string }`

程序将用户输入的指令写入日志。

- `type`: `"SERVER.COMMAND"`
- `type`: `"MISC.COMMAND"`
- `time`: 同上。
- `command`: 输入的指令。

## 3.4 Client Stop

`{ type: "SERVER.CLIENT_STOP", time: time }`
`{ type: "MISC.CLIENT_STOP", time: time }`

客户端正常关闭时将事件写入日志。

- `type`: `"MISC.SERVER.CLIENT_STOP"`
- `type`: `"MISC.MISC.CLIENT_STOP"`
- `time`: 同上。

## 3.5 Stop
## 3.5 Server Stop

服务端正常关闭时的协议。

### 3.5.1 Announce

`{ type: "SERVER.STOP.ANNOUNCE" }`
`{ type: "MISC.SERVER_STOP.ANNOUNCE" }`

服务端正常关闭时,向全体客户端进行广播。

- `type`: `"SERVER.STOP.ANNOUNCE"`
- `type`: `"MISC.SERVER_STOP.ANNOUNCE"`

### 3.5.2 Log

`{ type: "SERVER.STOP.LOG", time: time }`
`{ type: "MISC.SERVER_STOP.LOG", time: time }`

服务端正常关闭时将事件写入日志。

- `type`: `"SERVER.STOP.LOG"`
- `type`: `"MISC.SERVER_STOP.LOG"`
- `time`: 同上。

## 3.6 Config

### 3.6.1 Post

`{ type: "SERVER.CONFIG.POST", key: string, value: any }`
`{ type: "MISC.CONFIG.POST", key: string, value: any }`

管理员向服务端发送配置修改请求。

- `type`: `"SERVER.CONFIG.POST"`
- `type`: `"MISC.CONFIG.POST"`
- `key`: 配置项名称。(下同)
- `value`: 配置值。(下同)

### 3.6.2 Change

`{ type: "SERVER.CONFIG.CHANGE", key: string, value: any, operator: number }`
`{ type: "MISC.CONFIG.CHANGE", key: string, value: any, operator: number }`

服务端向客户端广播配置修改事件。

- `type`: `"SERVER.CONFIG.CHANGE"`
- `type`: `"MISC.CONFIG.CHANGE"`
- `key`: 同上。
- `value`: 同上。
- `operator`: 执行修改操作的用户 ID。

### 3.6.3 Save

`{ type: "SERVER.CONFIG.SAVE", time: time }`
`{ type: "MISC.CONFIG.SAVE", time: time }`

服务端将聊天室房主导出配置的事件写入日志。

- `type`: `"SERVER.CONFIG.SAVE"`
- `type`: `"MISC.CONFIG.SAVE"`
- `time`: 同上。

### 3.6.4 Log

`{ type: "SERVER.CONFIG.LOG", time: time, key: string, value: any, operator: number }`
`{ type: "MISC.CONFIG.LOG", time: time, key: string, value: any, operator: number }`

服务端将配置修改事件写入日志。

- `type`: `"SERVER.CONFIG.LOG"`
- `type`: `"MISC.CONFIG.LOG"`
- `time`: 同上。
- `key`: 同上。
- `value`: 同上。
Expand Down Expand Up @@ -360,7 +362,7 @@
import time

# 程序版本
VERSION = "v4.8.0"
VERSION = "v4.9.0"

# 用于客户端解析协议 1.2
RESULTS = \
Expand Down Expand Up @@ -982,7 +984,7 @@ def process(message):
prints("\033[0m\033[1;36m再见!\033[0m")
exit_flag = True
return
if message["type"] == "SERVER.CONFIG.CHANGE": # 服务端参数变更 (协议 3.6.2)
if message["type"] == "MISC.CONFIG.CHANGE": # 服务端参数变更 (协议 3.6.2)
announce(message["operator"])
prints("配置项 {} 变更为:".format(message["key"]) + str(message["value"]), "cyan")
if side == "Client": # 同上
Expand All @@ -993,7 +995,7 @@ def process(message):
prints("该配置项相比修改前移除了:{}".format(str(deletions)), "cyan")
config[message["key"].split(".")[0]][message["key"].split(".")[1]] = message["value"]
return
if message["type"] == "SERVER.STOP.ANNOUNCE": # 服务端关闭 (协议 3.5.1)
if message["type"] == "MISC.SERVER_STOP.ANNOUNCE": # 服务端关闭 (协议 3.5.1)
if side == "Client": # 同上
announce(0)
prints("聊天室服务端已经关闭。", "cyan")
Expand Down Expand Up @@ -1132,7 +1134,7 @@ def do_doorman(arg, verbose=True, by=-1):
for i in range(len(users)):
users_abstract.append({"username": users[i]["username"], "status": users[i]["status"]})
send_queue.put(json.dumps({"to": arg[1], "content": {"type": "GATE.REVIEW_RESULT", "accepted": True, "operator": {"username": users[by]["username"], "uid": by}}})) # 协议 1.3
send_queue.put(json.dumps({"to": arg[1], "content": {"type": "SERVER.DATA", "server_version": VERSION, "uid": arg[1], "config": config, "users": users_abstract, "chat_history": history}})) # 协议 3.2
send_queue.put(json.dumps({"to": arg[1], "content": {"type": "MISC.DATA", "server_version": VERSION, "uid": arg[1], "config": config, "users": users_abstract, "chat_history": history}})) # 协议 3.2
if side == "Client":
upload({"type": "GATE.STATUS_CHANGE.REQUEST", "status": "Online", "uid": arg[1]}) # 协议 1.6.1

Expand Down Expand Up @@ -1313,12 +1315,12 @@ def do_config(arg, verbose=True, by=-1):
first, second = arg[0].split(".")
if side == "Server":
config[first][second] = eval(arg[1])
log_queue.put(json.dumps({"type": "SERVER.CONFIG.LOG", "time": time_str(), "key": first + "." + second, "value": eval(arg[1]), "operator": by})) # 协议 3.6.4
log_queue.put(json.dumps({"type": "MISC.CONFIG.LOG", "time": time_str(), "key": first + "." + second, "value": eval(arg[1]), "operator": by})) # 协议 3.6.4
for i in range(len(users)):
if users[i]["status"] in ["Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.CONFIG.CHANGE", "key": first + "." + second, "value": eval(arg[1]), "operator": by}})) # 协议 3.6.2
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.CONFIG.CHANGE", "key": first + "." + second, "value": eval(arg[1]), "operator": by}})) # 协议 3.6.2
if side == "Client":
upload({"type": "SERVER.CONFIG.POST", "key": first + "." + second, "value": eval(arg[1])}) # 协议 3.6.1
upload({"type": "MISC.CONFIG.POST", "key": first + "." + second, "value": eval(arg[1])}) # 协议 3.6.1
except:
printc(verbose, "指令格式不正确,请重试。")
return
Expand Down Expand Up @@ -1359,28 +1361,28 @@ def do_ban(arg, verbose=True, by=-1):
if side == "Server":
ips = [item for item in ips if item not in config["ban"]["ip"]]
config["ban"]["ip"] += ips
log_queue.put(json.dumps({"type": "SERVER.CONFIG.LOG", "time": time_str(), "key": "ban.ip", "value": config["ban"]["ip"], "operator": by})) # 协议 3.6.4
log_queue.put(json.dumps({"type": "MISC.CONFIG.LOG", "time": time_str(), "key": "ban.ip", "value": config["ban"]["ip"], "operator": by})) # 协议 3.6.4
for i in range(len(users)):
if users[i]["status"] in ["Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.CONFIG.CHANGE", "key": "ban.ip", "value": config["ban"]["ip"], "operator": by}})) # 协议 3.6.2
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.CONFIG.CHANGE", "key": "ban.ip", "value": config["ban"]["ip"], "operator": by}})) # 协议 3.6.2
if side == "Client":
ips = [item for item in ips if item not in config["ban"]["ip"]]
new_value = config["ban"]["ip"] + ips
upload({"type": "SERVER.CONFIG.POST", "key": "ban.ip", "value": new_value}) # 协议 3.6.1
upload({"type": "MISC.CONFIG.POST", "key": "ban.ip", "value": new_value}) # 协议 3.6.1
printc(verbose, "操作成功,共计封禁了 {} 个 IP 地址。".format(len(ips)))

if arg[1] == "remove":
if side == "Server":
ips = [item for item in ips if item in config["ban"]["ip"]]
config["ban"]["ip"] = [item for item in config["ban"]["ip"] if not item in ips]
log_queue.put(json.dumps({"type": "SERVER.CONFIG.LOG", "time": time_str(), "key": "ban.ip", "value": config["ban"]["ip"], "operator": by})) # 协议 3.6.4
log_queue.put(json.dumps({"type": "MISC.CONFIG.LOG", "time": time_str(), "key": "ban.ip", "value": config["ban"]["ip"], "operator": by})) # 协议 3.6.4
for i in range(len(users)):
if users[i]["status"] in ["Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.CONFIG.CHANGE", "key": "ban.ip", "value": config["ban"]["ip"], "operator": by}})) # 协议 3.6.2
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.CONFIG.CHANGE", "key": "ban.ip", "value": config["ban"]["ip"], "operator": by}})) # 协议 3.6.2
if side == "Client":
ips = [item for item in ips if item in config["ban"]["ip"]]
new_value = [item for item in config["ban"]["ip"] if not item in ips]
upload({"type": "SERVER.CONFIG.POST", "key": "ban.ip", "value": new_value}) # 协议 3.6.1
upload({"type": "MISC.CONFIG.POST", "key": "ban.ip", "value": new_value}) # 协议 3.6.1
printc(verbose, "操作成功,共计解除封禁了 {} 个 IP 地址。".format(len(ips)))

if arg[0] == "words":
Expand All @@ -1402,14 +1404,14 @@ def do_ban(arg, verbose=True, by=-1):
return
if side == "Server":
config["ban"]["words"].append(arg[2])
log_queue.put(json.dumps({"type": "SERVER.CONFIG.LOG", "time": time_str(), "key": "ban.words", "value": config["ban"]["words"], "operator": by})) # 协议 3.6.4
log_queue.put(json.dumps({"type": "MISC.CONFIG.LOG", "time": time_str(), "key": "ban.words", "value": config["ban"]["words"], "operator": by})) # 协议 3.6.4
for i in range(len(users)):
if users[i]["status"] in ["Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.CONFIG.CHANGE", "key": "ban.words", "value": config["ban"]["words"], "operator": by}})) # 协议 3.6.2
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.CONFIG.CHANGE", "key": "ban.words", "value": config["ban"]["words"], "operator": by}})) # 协议 3.6.2
if side == "Client":
new_value = config["ban"]["words"][:]
new_value.append(arg[2])
upload({"type": "SERVER.CONFIG.POST", "key": "ban.words", "value": new_value}) # 协议 3.6.1
upload({"type": "MISC.CONFIG.POST", "key": "ban.words", "value": new_value}) # 协议 3.6.1
printc(verbose, "操作成功。")

if arg[1] == "remove":
Expand All @@ -1418,14 +1420,14 @@ def do_ban(arg, verbose=True, by=-1):
return
if side == "Server":
config["ban"]["words"].remove(arg[2])
log_queue.put(json.dumps({"type": "SERVER.CONFIG.LOG", "time": time_str(), "key": "ban.words", "value": config["ban"]["words"], "operator": by})) # 协议 3.6.4
log_queue.put(json.dumps({"type": "MISC.CONFIG.LOG", "time": time_str(), "key": "ban.words", "value": config["ban"]["words"], "operator": by})) # 协议 3.6.4
for i in range(len(users)):
if users[i]["status"] in ["Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.CONFIG.CHANGE", "key": "ban.words", "value": config["ban"]["words"], "operator": by}})) # 协议 3.6.2
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.CONFIG.CHANGE", "key": "ban.words", "value": config["ban"]["words"], "operator": by}})) # 协议 3.6.2
if side == "Client":
new_value = config["ban"]["words"][:]
new_value.remove(arg[2])
upload({"type": "SERVER.CONFIG.POST", "key": "ban.words", "value": new_value}) # 协议 3.6.1
upload({"type": "MISC.CONFIG.POST", "key": "ban.words", "value": new_value}) # 协议 3.6.1
printc(verbose, "操作成功。")

def do_broadcast(arg, message=None, verbose=True, by=-1):
Expand Down Expand Up @@ -1751,7 +1753,7 @@ def do_save(arg=None):
with open("./config.json", "w", encoding="utf-8") as f:
json.dump(config, f)
print("参数已经成功保存到配置文件 config.json,下次启动时将自动加载配置项。")
log_queue.put(json.dumps({"type": "SERVER.CONFIG.SAVE", "time": time_str()})) # 协议 3.5
log_queue.put(json.dumps({"type": "MISC.CONFIG.SAVE", "time": time_str()})) # 协议 3.5
except:
print("无法将参数保存到配置文件 config.json,请稍后重试。")

Expand All @@ -1770,13 +1772,13 @@ def do_exit(arg=None):
# 防止干扰用户后续的终端使用
print("\033[0m\033[1;36m再见!\033[0m")
if side == "Server":
log_queue.put(json.dumps({"type": "SERVER.STOP.LOG", "time": time_str()})) # 协议 3.2.2
log_queue.put(json.dumps({"type": "MISC.SERVER_STOP.LOG", "time": time_str()})) # 协议 3.2.2
for i in range(len(users)):
if users[i]["status"] in ["Pending", "Online", "Admin", "Root"]:
send_queue.put(json.dumps({"to": i, "content": {"type": "SERVER.STOP.ANNOUNCE"}})) # 协议 3.2.1
send_queue.put(json.dumps({"to": i, "content": {"type": "MISC.SERVER_STOP.ANNOUNCE"}})) # 协议 3.2.1
server_socket.close()
if side == "Client":
log_queue.put(json.dumps({"type": "SERVER.CLIENT_STOP", "time": time_str()})) # 协议 3.4
log_queue.put(json.dumps({"type": "MISC.CLIENT_STOP", "time": time_str()})) # 协议 3.4
exit_flag = True
my_socket.close()
return
Expand Down Expand Up @@ -1966,7 +1968,7 @@ def thread_gate():
users_abstract = []
for i in range(len(users)):
users_abstract.append({"username": users[i]["username"], "status": users[i]["status"]})
send_queue.put(json.dumps({"to": uid, "content": {"type": "SERVER.DATA", "server_version": VERSION, "uid": uid, "config": config, "users": users_abstract, "chat_history": history}})) # 协议 3.2
send_queue.put(json.dumps({"to": uid, "content": {"type": "MISC.DATA", "server_version": VERSION, "uid": uid, "config": config, "users": users_abstract, "chat_history": history}})) # 协议 3.2

def thread_process():
global online_count
Expand Down Expand Up @@ -2002,7 +2004,7 @@ def thread_process():
do_doorman("reject " + str(content["uid"]), False, sender)
if content["status"] == "Online":
do_doorman("accept " + str(content["uid"]), False, sender)
if content["type"] == "SERVER.CONFIG.POST": # 协议 3.6.1
if content["type"] == "MISC.CONFIG.POST": # 协议 3.6.1
do_config("{} {}".format(content["key"], repr(content["value"])), False, sender)

def thread_receive():
Expand Down Expand Up @@ -2165,7 +2167,7 @@ def thread_input():
print("\033[8;30m", end="", flush=True)
blocked = False
continue
log_queue.put(json.dumps({"type": "SERVER.COMMAND", "time": time_str(), "command": command})) # 协议 3.3
log_queue.put(json.dumps({"type": "MISC.COMMAND", "time": time_str(), "command": command})) # 协议 3.3

# 将缩写形式替换为完整形式
for i in list(ABBREVIATION_TABLE.keys()):
Expand Down Expand Up @@ -2477,7 +2479,7 @@ def main():
sys.exit(1)

with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.START", "time": time_str(), "stamp": stamp, "server_version": VERSION, "config": config}) + "\n") # 协议 3.1
file.write(json.dumps({"type": "MISC.START", "time": time_str(), "stamp": stamp, "version": VERSION, "config": config}) + "\n") # 协议 3.1

side = "Server"
prints("启动成功!", "green")
Expand Down Expand Up @@ -2597,7 +2599,7 @@ def main():
my_socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, 30)

with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.START", "time": time_str(), "stamp": stamp, "server_version": VERSION, "config": config}) + "\n") # 协议 3.1
file.write(json.dumps({"type": "MISC.START", "time": time_str(), "stamp": stamp, "version": VERSION, "config": config}) + "\n") # 协议 3.1

# 核验协议 1.2,获取加入请求结果
try:
Expand Down Expand Up @@ -2633,15 +2635,15 @@ def main():
if seconds_consumed == 11:
prints("(也有可能是对方服务器端口被防火墙拦截,请联系服务器所有者确认,或检查本地网络及防火墙设置。)", "red")
with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
file.write(json.dumps({"type": "MISC.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
input("\033[0m")
sys.exit(1)

if not message["result"] in ["Accepted", "Pending review"]:
print()
prints("连接失败:{}".format(RESULTS[message["result"]]), "red")
with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
file.write(json.dumps({"type": "MISC.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
input("\033[0m")
sys.exit(1)

Expand All @@ -2662,11 +2664,11 @@ def main():
if not message:
raise
# 特殊情况:聊天室服务端已经关闭 (协议 3.5.1)
if message["type"] == "SERVER.STOP.ANNOUNCE":
if message["type"] == "MISC.SERVER_STOP.ANNOUNCE":
prints("聊天室服务端已经关闭。", "red")
prints("连接失败。", "red")
with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
file.write(json.dumps({"type": "MISC.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
input("\033[0m")
sys.exit(1)
# 一般情况:人工审核完成 (协议 1.3)
Expand All @@ -2675,7 +2677,7 @@ def main():
prints("服务端管理员 {} (UID = {}) 拒绝了您的连接请求。".format(message["operator"]["username"], message["operator"]["uid"]), "red")
prints("连接失败。", "red")
with open("./log.ndjson", "a", encoding="utf-8") as file:
file.write(json.dumps({"type": "SERVER.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
file.write(json.dumps({"type": "MISC.CLIENT_STOP", "time": time_str()}) + "\n") # 协议 3.4
input("\033[0m")
sys.exit(1)
if message["accepted"]:
Expand Down
Loading