.版本 2

.程序集 窗口程序集_启动窗口
.程序集变量 待发送命令, 文本型
.程序集变量 等待轮询客户, 文本型
.程序集变量 轮询开始时间, 整数型
.程序集变量 缓存客户, 文本型
.程序集变量 请求缓存, 字节集
.程序集变量 账号头像地址, 文本型
.程序集变量 待加载头像地址, 文本型


.子程序 _服务器1_客户进入



.子程序 _服务器1_客户离开
.局部变量 客户地址, 文本型

客户地址 ＝ 服务器1.取回客户 ()
清除请求缓存 (客户地址)

.如果真 (客户地址 ＝ 等待轮询客户)
    等待轮询客户 ＝ “”
    轮询开始时间 ＝ 0
    写日志 (“等待中的 poll 连接已断开”)
.如果真结束



.子程序 _服务器1_数据到达
.局部变量 客户地址, 文本型
.局部变量 收到数据, 字节集
.局部变量 完整请求, 字节集
.局部变量 请求文本, 文本型

客户地址 ＝ 服务器1.取回客户 ()
收到数据 ＝ 服务器1.取回数据 ()

.如果 (缓存客户 ＝ “”)
    缓存客户 ＝ 客户地址
    请求缓存 ＝ 收到数据
.否则
    .如果 (缓存客户 ＝ 客户地址)
        请求缓存 ＝ 请求缓存 ＋ 收到数据
    .否则
        写日志 (“收到另一条 HTTP 请求，已丢弃上一条未完成请求”)
        服务器1.断开客户 (缓存客户)
        缓存客户 ＝ 客户地址
        请求缓存 ＝ 收到数据
    .如果结束
.如果结束

.如果真 (请求是否完整 () ＝ 假)
    返回 ()
.如果真结束

完整请求 ＝ 请求缓存
缓存客户 ＝ “”
请求缓存 ＝ 到字节集 (“”)
请求文本 ＝ UTF8到文本 (完整请求)
处理HTTP请求 (客户地址, 请求文本)



.子程序 __启动窗口_创建完毕

待发送命令 ＝ “”
等待轮询客户 ＝ “”
轮询开始时间 ＝ 0
缓存客户 ＝ “”
请求缓存 ＝ 到字节集 (“”)
账号头像地址 ＝ “”
待加载头像地址 ＝ “”
写日志 (“服务已启动，监听端口 8000”)



.子程序 _按钮_查询账号_被单击

提交命令 (构造命令 (“account_status”, “{}”), “查询当前账号资料”)



.子程序 _按钮_发送文本_被单击
.局部变量 参数JSON, 文本型

.如果真 (编辑框_接收人.内容 ＝ “”)
    信息框 (“请输入接收人 wxid、群 ID 或 filehelper”, 0, “提示”)
    返回 ()
.如果真结束

.如果真 (编辑框_文本内容.内容 ＝ “”)
    信息框 (“请输入文本消息内容”, 0, “提示”)
    返回 ()
.如果真结束

参数JSON ＝ “{” ＋ #引号 ＋ “receiver” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_接收人.内容) ＋ #引号 ＋ “,” ＋ #引号 ＋ “content” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_文本内容.内容) ＋ #引号 ＋ “}”
提交命令 (构造命令 (“send_message”, 参数JSON), “发送文本到 ” ＋ 编辑框_接收人.内容)



.子程序 _按钮_发送图片_被单击
.局部变量 参数JSON, 文本型

.如果真 (编辑框_接收人.内容 ＝ “”)
    信息框 (“请输入接收人”, 0, “提示”)
    返回 ()
.如果真结束

.如果真 (编辑框_图片地址.内容 ＝ “”)
    信息框 (“请输入图片 URL 或本地绝对路径”, 0, “提示”)
    返回 ()
.如果真结束

参数JSON ＝ “{” ＋ #引号 ＋ “receiver” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_接收人.内容) ＋ #引号 ＋ “,” ＋ #引号 ＋ “image_path” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_图片地址.内容) ＋ #引号 ＋ “}”
提交命令 (构造命令 (“send_image”, 参数JSON), “发送图片到 ” ＋ 编辑框_接收人.内容)



.子程序 _按钮_发送XML_被单击
.局部变量 参数JSON, 文本型
.局部变量 转发JSON, 文本型
.局部变量 模式说明, 文本型

.如果真 (编辑框_接收人.内容 ＝ “”)
    信息框 (“请输入接收人”, 0, “提示”)
    返回 ()
.如果真结束

.如果真 (编辑框_XML内容.内容 ＝ “”)
    信息框 (“请输入 appmsg XML”, 0, “提示”)
    返回 ()
.如果真结束

.如果 (选择框_XML转发.选中)
    转发JSON ＝ “true”
    模式说明 ＝ “转发模式”
.否则
    转发JSON ＝ “false”
    模式说明 ＝ “直接发送模式”
.如果结束

参数JSON ＝ “{” ＋ #引号 ＋ “receiver” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_接收人.内容) ＋ #引号 ＋ “,” ＋ #引号 ＋ “type” ＋ #引号 ＋ “:33,” ＋ #引号 ＋ “forward” ＋ #引号 ＋ “:” ＋ 转发JSON ＋ “,” ＋ #引号 ＋ “xml” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (编辑框_XML内容.内容) ＋ #引号 ＋ “}”
提交命令 (构造命令 (“send_xml”, 参数JSON), “发送 XML（” ＋ 模式说明 ＋ “）到 ” ＋ 编辑框_接收人.内容)



.子程序 _按钮_清空日志_被单击

编辑框_日志.内容 ＝ “”



.子程序 _时钟1_周期事件
.局部变量 已等待毫秒, 整数型
.局部变量 客户地址, 文本型

.如果真 (待加载头像地址 ≠ “”)
    加载账号头像 ()
.如果真结束

.如果真 (等待轮询客户 ＝ “”)
    返回 ()
.如果真结束

.如果真 (待发送命令 ≠ “”)
    尝试立即下发 ()
    返回 ()
.如果真结束

已等待毫秒 ＝ 取启动时间 () － 轮询开始时间

.如果真 (已等待毫秒 ＜ 0)
    已等待毫秒 ＝ 20000
.如果真结束

.如果真 (已等待毫秒 ≥ 20000)
    客户地址 ＝ 等待轮询客户
    等待轮询客户 ＝ “”
    轮询开始时间 ＝ 0
    发送JSON响应 (客户地址, 构造轮询响应 (“”))
    写日志 (“poll 等待 20 秒超时，已返回空命令”)
.如果真结束



.子程序 处理HTTP请求
.参数 客户地址, 文本型
.参数 请求文本, 文本型
.局部变量 正文, 文本型
.局部变量 响应内容, 文本型
.局部变量 旧轮询客户, 文本型

正文 ＝ 取HTTP正文 (请求文本)

.如果真 (寻找文本 (请求文本, “GET /health ”, , 假) ≠ -1)
    发送JSON响应 (客户地址, “{” ＋ #引号 ＋ “ok” ＋ #引号 ＋ “:true}”)
    返回 ()
.如果真结束

.如果真 (寻找文本 (请求文本, “POST /agent/register ”, , 假) ≠ -1)
    写日志 (“[插件注册] ” ＋ 正文)
    更新账号信息 (正文)
    发送JSON响应 (客户地址, “{}”)
    .如果真 (待发送命令 ＝ “”)
        提交命令 (构造命令 (“account_status”, “{}”), “注册成功后自动查询账号资料”)
    .如果真结束
    返回 ()
.如果真结束

.如果真 (寻找文本 (请求文本, “POST /agent/state ”, , 假) ≠ -1)
    写日志 (“[状态上报] ” ＋ 正文)
    发送JSON响应 (客户地址, “{}”)
    返回 ()
.如果真结束

.如果真 (寻找文本 (请求文本, “POST /agent/event ”, , 假) ≠ -1)
    写日志 (“[主动事件] ” ＋ 正文)
    发送JSON响应 (客户地址, “{}”)
    返回 ()
.如果真结束

.如果真 (寻找文本 (请求文本, “POST /agent/command_result ”, , 假) ≠ -1)
    写日志 (“[命令结果] ” ＋ 正文)
    .如果真 (寻找文本 (正文, #引号 ＋ “command_name” ＋ #引号 ＋ “:” ＋ #引号 ＋ “account_status” ＋ #引号, , 假) ≠ -1)
        更新账号信息 (正文)
    .如果真结束
    发送JSON响应 (客户地址, “{}”)
    返回 ()
.如果真结束

.如果真 (寻找文本 (请求文本, “POST /agent/poll ”, , 假) ≠ -1)
    .如果 (待发送命令 ≠ “”)
        响应内容 ＝ 构造轮询响应 (待发送命令)
        写日志 (“命令响应字符数：” ＋ 到文本 (取文本长度 (响应内容)))
        .如果 (发送JSON响应 (客户地址, 响应内容))
            待发送命令 ＝ “”
            写日志 (“已有命令，已在本次 poll 中下发”)
        .否则
            写日志 (“命令下发失败，保留到下一次 poll 重试”)
        .如果结束
    .否则
        .如果真 (等待轮询客户 ≠ “”)
            旧轮询客户 ＝ 等待轮询客户
            等待轮询客户 ＝ “”
            发送JSON响应 (旧轮询客户, 构造轮询响应 (“”))
            写日志 (“旧 poll 已结束，替换为新连接”)
        .如果真结束

        等待轮询客户 ＝ 客户地址
        轮询开始时间 ＝ 取启动时间 ()
        写日志 (“无待发命令，开始保持 poll 连接”)
    .如果结束
    返回 ()
.如果真结束

写日志 (“[未知接口] ” ＋ 取文本左边 (请求文本, 200))
发送JSON响应 (客户地址, “{}”)



.子程序 提交命令, 逻辑型
.参数 命令文本, 文本型
.参数 日志说明, 文本型

.如果真 (待发送命令 ≠ “”)
    写日志 (“当前已有待发命令，请等待插件取走后再操作”)
    返回 (假)
.如果真结束

待发送命令 ＝ 命令文本
写日志 (“[命令排队] ” ＋ 日志说明)
尝试立即下发 ()
返回 (真)



.子程序 尝试立即下发
.局部变量 客户地址, 文本型
.局部变量 响应内容, 文本型

.如果真 (待发送命令 ＝ “” 或 等待轮询客户 ＝ “”)
    返回 ()
.如果真结束

客户地址 ＝ 等待轮询客户
等待轮询客户 ＝ “”
轮询开始时间 ＝ 0
响应内容 ＝ 构造轮询响应 (待发送命令)
写日志 (“命令响应字符数：” ＋ 到文本 (取文本长度 (响应内容)))

.如果 (发送JSON响应 (客户地址, 响应内容))
    待发送命令 ＝ “”
    写日志 (“命令已通过等待中的 poll 立即下发”)
.否则
    写日志 (“等待中的 poll 发送失败，命令保留等待重试”)
.如果结束



.子程序 构造命令, 文本型
.参数 命令名称, 文本型
.参数 参数JSON, 文本型
.局部变量 请求ID, 文本型

请求ID ＝ “e-” ＋ 命令名称 ＋ “-” ＋ 到文本 (取启动时间 ())
返回 (“{” ＋ #引号 ＋ “request_id” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (请求ID) ＋ #引号 ＋ “,” ＋ #引号 ＋ “command_name” ＋ #引号 ＋ “:” ＋ #引号 ＋ JSON转义 (命令名称) ＋ #引号 ＋ “,” ＋ #引号 ＋ “args” ＋ #引号 ＋ “:” ＋ 参数JSON ＋ “}”)



.子程序 构造轮询响应, 文本型
.参数 命令文本, 文本型

.如果真 (命令文本 ＝ “”)
    返回 (“{” ＋ #引号 ＋ “commands” ＋ #引号 ＋ “:[]}”)
.如果真结束

返回 (“{” ＋ #引号 ＋ “commands” ＋ #引号 ＋ “:[” ＋ 命令文本 ＋ “]}”)



.子程序 发送JSON响应, 逻辑型
.参数 客户地址, 文本型
.参数 响应内容, 文本型
.局部变量 响应内容UTF8, 字节集
.局部变量 响应头, 文本型
.局部变量 响应数据, 字节集
.局部变量 发送成功, 逻辑型

响应内容UTF8 ＝ 文本到UTF8 (响应内容)
响应头 ＝ “HTTP/1.1 200 OK” ＋ #换行符 ＋ “Content-Type: application/json; charset=utf-8” ＋ #换行符 ＋ “Content-Length: ” ＋ 到文本 (取字节集长度 (响应内容UTF8)) ＋ #换行符 ＋ “Connection: close” ＋ #换行符 ＋ #换行符
响应数据 ＝ 到字节集 (响应头) ＋ 响应内容UTF8
发送成功 ＝ 服务器1.发送数据 (客户地址, 响应数据, )
服务器1.断开客户 (客户地址)
返回 (发送成功)



.子程序 清除请求缓存
.参数 客户地址, 文本型

.如果真 (缓存客户 ＝ 客户地址)
    缓存客户 ＝ “”
    请求缓存 ＝ 到字节集 (“”)
.如果真结束



.子程序 请求是否完整, 逻辑型
.局部变量 临时文本, 文本型
.局部变量 分隔文本, 文本型
.局部变量 头结束位置, 整数型
.局部变量 正文长度, 整数型
.局部变量 需要字节数, 整数型

.如果真 (缓存客户 ＝ “” 或 取字节集长度 (请求缓存) ＝ 0)
    返回 (假)
.如果真结束

临时文本 ＝ 到文本 (请求缓存)
分隔文本 ＝ #换行符 ＋ #换行符
头结束位置 ＝ 寻找文本 (临时文本, 分隔文本, , 假)

.如果真 (头结束位置 ＝ -1)
    返回 (假)
.如果真结束

正文长度 ＝ 取HTTP内容长度 (取文本左边 (临时文本, 头结束位置 － 1))
需要字节数 ＝ 头结束位置 － 1 ＋ 取文本长度 (分隔文本) ＋ 正文长度
返回 (取字节集长度 (请求缓存) ≥ 需要字节数)



.子程序 取HTTP内容长度, 整数型
.参数 请求头, 文本型
.局部变量 字段名, 文本型
.局部变量 字段位置, 整数型
.局部变量 数值开始, 整数型
.局部变量 行结束, 整数型
.局部变量 长度文本, 文本型

字段名 ＝ “Content-Length:”
字段位置 ＝ 寻找文本 (请求头, 字段名, , 真)

.如果真 (字段位置 ＝ -1)
    返回 (0)
.如果真结束

数值开始 ＝ 字段位置 ＋ 取文本长度 (字段名)
行结束 ＝ 寻找文本 (请求头, #换行符, 数值开始, 假)

.如果真 (行结束 ＝ -1)
    行结束 ＝ 取文本长度 (请求头) ＋ 1
.如果真结束

长度文本 ＝ 删首尾空 (取文本中间 (请求头, 数值开始, 行结束 － 数值开始))
返回 (到整数 (长度文本))



.子程序 取HTTP正文, 文本型
.参数 请求文本, 文本型
.局部变量 分隔文本, 文本型
.局部变量 分隔位置, 整数型
.局部变量 正文开始, 整数型

分隔文本 ＝ #换行符 ＋ #换行符
分隔位置 ＝ 寻找文本 (请求文本, 分隔文本, , 假)

.如果真 (分隔位置 ＝ -1)
    返回 (“”)
.如果真结束

正文开始 ＝ 分隔位置 ＋ 取文本长度 (分隔文本)
返回 (取文本中间 (请求文本, 正文开始, 取文本长度 (请求文本)))



.子程序 更新账号信息
.参数 JSON文本, 文本型
.局部变量 临时值, 文本型

临时值 ＝ 取JSON字符串值 (JSON文本, “account_id”)
.如果真 (临时值 ≠ “”)
    编辑框_账号WXID.内容 ＝ 临时值
.如果真结束

临时值 ＝ 取JSON字符串值 (JSON文本, “display_name”)
.如果真 (临时值 ＝ “”)
    临时值 ＝ 取JSON字符串值 (JSON文本, “nick_name”)
.如果真结束
.如果真 (临时值 ≠ “”)
    编辑框_账号昵称.内容 ＝ 临时值
.如果真结束

临时值 ＝ 取JSON字符串值 (JSON文本, “avatar_url”)
.如果真 (临时值 ≠ “” 且 临时值 ≠ 账号头像地址)
    账号头像地址 ＝ 临时值
    待加载头像地址 ＝ 临时值
.如果真结束

临时值 ＝ 取JSON字符串值 (JSON文本, “wechat_version”)
.如果真 (临时值 ≠ “”)
    编辑框_微信版本.内容 ＝ 临时值
.如果真结束



.子程序 加载账号头像
.局部变量 下载地址, 文本型
.局部变量 图片数据, 字节集

下载地址 ＝ 待加载头像地址
待加载头像地址 ＝ “”

.如果真 (下载地址 ＝ “”)
    返回 ()
.如果真结束

写日志 (“正在加载账号头像”)
图片数据 ＝ HTTP读文件 (下载地址)

.如果 (取字节集长度 (图片数据) ＞ 0)
    图片框_账号头像.图片 ＝ 图片数据
    写日志 (“账号头像加载成功，字节数：” ＋ 到文本 (取字节集长度 (图片数据)))
.否则
    写日志 (“账号头像加载失败：” ＋ 下载地址)
.如果结束



.子程序 取JSON字符串值, 文本型
.参数 JSON文本, 文本型
.参数 键名, 文本型
.局部变量 键标记, 文本型
.局部变量 键位置, 整数型
.局部变量 冒号位置, 整数型
.局部变量 引号位置, 整数型
.局部变量 结束引号, 整数型
.局部变量 原始值, 文本型

键标记 ＝ #引号 ＋ 键名 ＋ #引号
键位置 ＝ 寻找文本 (JSON文本, 键标记, , 假)
.如果真 (键位置 ＝ -1)
    返回 (“”)
.如果真结束

冒号位置 ＝ 寻找文本 (JSON文本, “:”, 键位置 ＋ 取文本长度 (键标记), 假)
.如果真 (冒号位置 ＝ -1)
    返回 (“”)
.如果真结束

引号位置 ＝ 寻找文本 (JSON文本, #引号, 冒号位置 ＋ 1, 假)
.如果真 (引号位置 ＝ -1)
    返回 (“”)
.如果真结束

结束引号 ＝ 寻找文本 (JSON文本, #引号, 引号位置 ＋ 1, 假)
.如果真 (结束引号 ＝ -1)
    返回 (“”)
.如果真结束

原始值 ＝ 取文本中间 (JSON文本, 引号位置 ＋ 1, 结束引号 － 引号位置 － 1)
返回 (JSON反转义 (原始值))



.子程序 JSON转义, 文本型
.参数 原文本, 文本型
.局部变量 结果, 文本型

结果 ＝ 子文本替换 (原文本, “\”, “\\”, , , 真)
结果 ＝ 子文本替换 (结果, #引号, “\” ＋ #引号, , , 真)
结果 ＝ 子文本替换 (结果, 字符 (8), “\b”, , , 真)
结果 ＝ 子文本替换 (结果, 字符 (9), “\t”, , , 真)
结果 ＝ 子文本替换 (结果, 字符 (10), “\n”, , , 真)
结果 ＝ 子文本替换 (结果, 字符 (12), “\f”, , , 真)
结果 ＝ 子文本替换 (结果, 字符 (13), “\r”, , , 真)
返回 (结果)



.子程序 JSON反转义, 文本型
.参数 原文本, 文本型
.局部变量 结果, 文本型

结果 ＝ 子文本替换 (原文本, “\/”, “/”, , , 真)
结果 ＝ 子文本替换 (结果, “\b”, 字符 (8), , , 真)
结果 ＝ 子文本替换 (结果, “\n”, #换行符, , , 真)
结果 ＝ 子文本替换 (结果, “\t”, 字符 (9), , , 真)
结果 ＝ 子文本替换 (结果, “\f”, 字符 (12), , , 真)
结果 ＝ 子文本替换 (结果, “\r”, 字符 (13), , , 真)
结果 ＝ 子文本替换 (结果, “\” ＋ #引号, #引号, , , 真)
结果 ＝ 子文本替换 (结果, “\\”, “\”, , , 真)
返回 (结果)



.子程序 写日志
.参数 日志文本, 文本型
.局部变量 完整行, 文本型

完整行 ＝ “[” ＋ 到文本 (取现行时间 ()) ＋ “] ” ＋ 日志文本

.如果 (编辑框_日志.内容 ＝ “”)
    编辑框_日志.内容 ＝ 完整行
.否则
    编辑框_日志.加入文本 (#换行符 ＋ 完整行)
.如果结束

.如果真 (取文本长度 (编辑框_日志.内容) ＞ 200000)
    编辑框_日志.内容 ＝ 取文本右边 (编辑框_日志.内容, 150000)
.如果真结束

编辑框_日志.起始选择位置 ＝ -1
