├── .DS_Store ├── .gitignore ├── Apps Script ├── .DS_Store ├── BotTest │ ├── BaseBot │ │ ├── README.md │ │ ├── getMessage.gs │ │ ├── setEchoBack.gs │ │ └── setMessageStock.gs │ ├── HighBot │ │ ├── Bot.gs │ │ ├── Functions.gs │ │ ├── Keyboard.gs │ │ ├── README.md │ │ ├── dealMessage.gs │ │ └── doPost.gs │ └── parseTextTest.gs ├── MaoBot@BetaVersion │ ├── MaoBotTest │ │ ├── maoBotKing.gs │ │ └── maoBotTest.gs │ ├── README.md │ └── maoBot.gs ├── MaoBot@OfficialVersion │ ├── .DS_Store │ ├── COURSE.md │ ├── DB │ │ ├── COURSE.md │ │ └── XiaoMaoBot_DB.xlsx │ ├── Modules │ │ ├── Api.gs │ │ ├── Core.gs │ │ ├── Manage.gs │ │ ├── MaoBot.gs │ │ ├── Params.gs │ │ └── Utils.gs │ └── README.md ├── README.md └── 配置图解 │ ├── Google Apps Script 脚本部署1.png │ ├── Google Apps Script 脚本部署2.png │ ├── Google表格加载脚本位置 - Google Apps Script.png │ ├── README.md │ ├── bot消息主动回复功能演示.png │ ├── 数据表结构 - Google表格.png │ └── 运行效果及数据存储.png ├── COURSE.md ├── README.md └── UPDATELOG.md /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | modelTest.gs 2 | mao.gs 3 | backup -------------------------------------------------------------------------------- /Apps Script/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/.DS_Store -------------------------------------------------------------------------------- /Apps Script/BotTest/BaseBot/README.md: -------------------------------------------------------------------------------- 1 | ##### 🎟 基础脚本 入门必学 2 | > 建议有js基础的同学可以从这学起 3 | > 4 | > 学完之后即可直接使用 [MaoBot.gs](https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script/MaoBot.gs)入门 -------------------------------------------------------------------------------- /Apps Script/BotTest/BaseBot/getMessage.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 用于获取讯息 3 | * 通过 Tg https://t.me/BotFather 机器人 获取bot ID 4 | * 替换下面链接ID,浏览器打开即可 5 | * https://api.telegram.org/bot你的BotID/getUpdates 6 | * 对机器人发送讯息,刷新网页即可看到结果 7 | */ 8 | -------------------------------------------------------------------------------- /Apps Script/BotTest/BaseBot/setEchoBack.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 回声机器人,将接收者信息进行原样返回 3 | * * 部署完后替换下面链接botID及部署后的webURL 4 | * https://api.telegram.org/bot你的botID/setWebhook?url=你的google webURL 5 | * 6 | * 浏览器打开链接,返回下方内容皆为成功 7 | * {"ok":true,"result":true,"description":"Webhook is set"} 8 | * {"ok":true,"result":true,"description":"Webhook is already set"} 9 | * 返回google表格即可看到新内容存储 10 | * 11 | * 返回下面内容则botID失效,通过BotFather重新获取 12 | * {"ok":false,"error_code":401,"description":"Unauthorized"} 13 | */ 14 | function doPost(e) { 15 | var estringa = JSON.parse(e.postData.contents); 16 | var payload = start(estringa); 17 | var data = { 18 | method: "post", 19 | payload: payload, 20 | }; 21 | UrlFetchApp.fetch("https://api.telegram.org/bot你的BotID/", data); 22 | } 23 | function start(estringa) { 24 | var id = estringa.message.from.id.toString(); 25 | var payload; 26 | 27 | if (estringa.message.text) { 28 | payload = { 29 | method: "sendMessage", 30 | chat_id: id, 31 | text: estringa.message.text, 32 | }; 33 | } else if (estringa.message.sticker) { 34 | payload = { 35 | method: "sendSticker", 36 | chat_id: id, 37 | sticker: estringa.message.sticker.file_id, 38 | }; 39 | } else if (estringa.message.photo) { 40 | array = estringa.message.photo; 41 | text = array[1]; 42 | payload = { 43 | method: "sendPhoto", 44 | chat_id: id, 45 | photo: text.file_id, 46 | }; 47 | } else { 48 | // 如果傳送的照片檔案太小(10幾KB),就不會有回聲效果 49 | payload = { 50 | method: "sendMessage", 51 | chat_id: id, 52 | text: "回声失败,请更换内容重试", 53 | }; 54 | } 55 | return payload; 56 | } 57 | -------------------------------------------------------------------------------- /Apps Script/BotTest/BaseBot/setMessageStock.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 用于接收讯息并进行exec存储 3 | * 表字段 4 | * A1 - 时间 5 | * B1 - 发送人ID 6 | * C1 - 接收到的内容 7 | * 8 | * 部署完后替换下面链接botID及部署后的webURL 9 | * https://api.telegram.org/bot你的botID/setWebhook?url=你的google webURL 10 | * 11 | * 浏览器打开链接,返回下方内容皆为成功 12 | * {"ok":true,"result":true,"description":"Webhook is set"} 13 | * {"ok":true,"result":true,"description":"Webhook is already set"} 14 | * 返回google表格即可看到新内容存储 15 | * 16 | * 返回下面内容则botID失效,通过BotFather重新获取 17 | * {"ok":false,"error_code":401,"description":"Unauthorized"} 18 | */ 19 | 20 | function doPost(e){ 21 | var estringa = JSON.parse(e.postData.contents); 22 | var d = new Date(); 23 | var e = estringa.message.from.id; 24 | //取自浏览器顶部路径 25 | // https://docs.google.com/spreadsheets/d/表单ID/edit#gid=0 26 | var SpreadSheet = SpreadsheetApp.openById("表单ID"); 27 | // 注意是表名,底下的表名,不是exec名字 28 | var Sheet = SpreadSheet.getSheetByName("EXEC工作表名"); 29 | var LastRow = Sheet.getLastRow(); 30 | Sheet.getRange(LastRow+1, 1).setValue(d); 31 | Sheet.getRange(LastRow+1, 2).setValue(e); 32 | Sheet.getRange(LastRow+1, 3).setValue(estringa); 33 | } -------------------------------------------------------------------------------- /Apps Script/BotTest/HighBot/Bot.gs: -------------------------------------------------------------------------------- 1 | var token = ""; 2 | var telegramUrl = "https://api.telegram.org/bot" + token; 3 | var webAppUrl = "https://script.google.com/macros/s//exec"; 4 | 5 | function getMe() { 6 | var url = telegramUrl + "/getMe"; 7 | var response = UrlFetchApp.fetch(url); 8 | Logger.log(response.getContentText()); 9 | } 10 | 11 | function setWebhook() { 12 | var url = telegramUrl + "/setWebhook?url=" + webAppUrl; 13 | var response = UrlFetchApp.fetch(url); 14 | Logger.log(response.getContentText()); 15 | } 16 | 17 | function deletWebhook() { 18 | var url = telegramUrl + "/setWebhook"; 19 | var response = UrlFetchApp.fetch(url); 20 | } 21 | 22 | function doGet(e) { 23 | return HtmlService.createHtmlOutput("Webhook is working."); 24 | } 25 | 26 | // Send message 27 | function sendMessage(chat_id, text) { 28 | var data = { 29 | method: "post", 30 | payload: { 31 | method: "sendMessage", 32 | chat_id: String(chat_id), 33 | text: text, 34 | parse_mode: "HTML", 35 | disable_web_page_preview: true, 36 | }, 37 | }; 38 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 39 | } 40 | 41 | // Delete message 42 | function deleteMessage(chat_id, message_id) { 43 | var data = { 44 | method: "post", 45 | payload: { 46 | method: "deleteMessage", 47 | chat_id: String(chat_id), 48 | message_id: parseInt(message_id, 10), 49 | }, 50 | }; 51 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 52 | } 53 | 54 | // Edit message 55 | function editMessage(chat_id, message_id, text) { 56 | var data = { 57 | method: "post", 58 | payload: { 59 | method: "editMessageText", 60 | chat_id: String(chat_id), 61 | message_id: parseInt(message_id, 10), 62 | text: text, 63 | parse_mode: "HTML", 64 | disable_web_page_preview: true, 65 | }, 66 | }; 67 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 68 | } 69 | 70 | // Send message and keyboard 71 | function sendMessageKeyboard(chat_id, text, keyboard) { 72 | var data = { 73 | method: "post", 74 | payload: { 75 | method: "sendMessage", 76 | chat_id: String(chat_id), 77 | text: text, 78 | parse_mode: "HTML", 79 | reply_markup: JSON.stringify(keyboard), 80 | }, 81 | }; 82 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 83 | } 84 | 85 | // Hide keyboard 86 | function hideKeyBoard(chat_id, message_id) { 87 | var data = { 88 | method: "post", 89 | payload: { 90 | method: "editMessageReplyMarkup", 91 | chat_id: String(chat_id), 92 | message_id: parseInt(message_id, 10), 93 | }, 94 | }; 95 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 96 | } 97 | 98 | // Replace keyboard 99 | function replaceKeyBoard(chat_id, message_id, keyboard) { 100 | var data = { 101 | method: "post", 102 | payload: { 103 | method: "editMessageReplyMarkup", 104 | chat_id: String(chat_id), 105 | message_id: parseInt(message_id, 10), 106 | reply_markup: JSON.stringify(keyboard), 107 | }, 108 | }; 109 | UrlFetchApp.fetch("https://api.telegram.org/bot" + token + "/", data); 110 | } 111 | -------------------------------------------------------------------------------- /Apps Script/BotTest/HighBot/Functions.gs: -------------------------------------------------------------------------------- 1 | // Global 2 | var todayMonth = getMonthToday(); 3 | var NEWL = String.fromCharCode(10); // newline 4 | 5 | var accId = "= 0) { 156 | payload.text = 157 | "JS神技能 - https://www.youtube.com/channel/UC6tPP3jOTKgjqfDgqMsaG4g"; 158 | } 159 | if ("悟空的日常".toLowerCase().indexOf(paras[2]) >= 0) { 160 | payload.text = 161 | "悟空的日常 - https://www.youtube.com/channel/UCii04BCvYIdQvshrdNDAcww"; 162 | } 163 | if ("YuFeng Deng".toLowerCase().indexOf(paras[2]) >= 0) { 164 | payload.text = 165 | "YuFeng Deng - https://www.youtube.com/channel/UCG6xoef2xU86hnrCsS5m5Cw"; 166 | } 167 | } else { 168 | payload.text = "JS神技能\n" + "悟空的日常\n" + "YuFeng Deng\n"; 169 | return payload; 170 | } 171 | break; 172 | default: 173 | payload.text = "红\n黄\n蓝"; 174 | break; 175 | } 176 | 177 | return payload; 178 | } else { 179 | payload.text = 180 | "*JS神技能*\n" + 181 | "[悟空的日常](https://www.youtube.com/channel/UCii04BCvYIdQvshrdNDAcww)\n" + 182 | "[*YuFeng Deng*](https://www.youtube.com/channel/UCG6xoef2xU86hnrCsS5m5Cw)\n" + 183 | "_YuFeng Deng_\n" + 184 | "`01|" + 185 | "UCii04BCvYIdQvshrdNDAcww" + 186 | " | `\n" + 187 | "`02|" + 188 | "UCG6xoef2xU86hnrCsS5m5Cw" + 189 | " | `\n" + 190 | "```javascript\n" + 191 | "payload = {\n" + 192 | ' "method": "sendMessage",\n' + 193 | ' "chat_id": body.message.chat.id,\n' + 194 | ' "text": body.message.text,\n' + 195 | "}" + 196 | "```"; 197 | 198 | var inlineKeyboardMarkup = {}; 199 | inlineKeyboardMarkup.inline_keyboard = []; 200 | var keyboardRow = []; 201 | var keyboardButton1 = { 202 | text: "按钮1", 203 | url: "https://www.google.com", 204 | }; 205 | 206 | var keyboardButton2 = { 207 | text: "按钮2", 208 | url: "https://www.google.com", 209 | }; 210 | 211 | var keyboardRow2 = []; 212 | var keyboardButton3 = { 213 | text: "按钮3", 214 | url: "https://www.google.com", 215 | }; 216 | 217 | var keyboardButton4 = { 218 | text: "按钮4", 219 | url: "https://www.google.com", 220 | }; 221 | 222 | keyboardRow.push(keyboardButton1); 223 | keyboardRow.push(keyboardButton2); 224 | 225 | keyboardRow2.push(keyboardButton3); 226 | keyboardRow2.push(keyboardButton4); 227 | inlineKeyboardMarkup.inline_keyboard.push(keyboardRow); 228 | inlineKeyboardMarkup.inline_keyboard.push(keyboardRow2); 229 | payload.reply_markup = inlineKeyboardMarkup; 230 | 231 | return payload; 232 | } 233 | } 234 | 235 | payload = { 236 | method: "sendMessage", 237 | chat_id: body.message.chat.id, 238 | text: body.message.text, 239 | }; 240 | } else if (body.message.sticker) { 241 | payload = { 242 | method: "sendSticker", 243 | chat_id: body.message.chat.id, 244 | sticker: body.message.sticker.file_id, 245 | }; 246 | } else if (body.message.photo) { 247 | array = body.message.photo; 248 | text = array[1]; 249 | payload = { 250 | method: "sendPhoto", 251 | chat_id: body.message.chat.id, 252 | photo: text.file_id, 253 | }; 254 | } else { 255 | payload = { 256 | method: "sendMessage", 257 | chat_id: body.message.chat.id, 258 | text: "Try other stuff", 259 | }; 260 | } 261 | return payload; 262 | } 263 | -------------------------------------------------------------------------------- /Apps Script/BotTest/HighBot/doPost.gs: -------------------------------------------------------------------------------- 1 | function doPost(e) { 2 | var contents = JSON.parse(e.postData.contents); 3 | 4 | if (contents.callback_query) { 5 | var message_id = contents.callback_query.message.message_id; 6 | var data = contents.callback_query.data; 7 | var multiData = data.split(" "); 8 | var chat_id = contents.callback_query.from.id; 9 | 10 | if (data == "Back") { 11 | replaceKeyBoard(chat_id, message_id, KEYBOARD_COMPANIES); 12 | } else if (data == "Cancel") { 13 | deleteMessage(chat_id, message_id); 14 | sendMessageKeyboard(chat_id, "Choose the action:", KEYBOARD_COMPANIES); 15 | } else if (data == "Common") { 16 | replaceKeyBoard(chat_id, message_id, KEYBOARD_COMMON); 17 | } else if (multiData[1] && multiData[0] == "Common") { 18 | deleteMessage(chat_id, message_id); 19 | sendMessageKeyboard( 20 | chat_id, 21 | "Expense: " + 22 | multiData[0] + 23 | " -> " + 24 | multiData[1] + 25 | ". Input the value:", 26 | KEYBOARD_INPUT 27 | ); 28 | } 29 | } else if (contents.message) { 30 | var chat_id = contents.message.from.id; 31 | var text = contents.message.text; 32 | 33 | if (text == "/start") { 34 | deleteMessage(chat_id, contents.message.message_id); 35 | sendMessageKeyboard(chat_id, "Choose the action:", KEYBOARD_COMPANIES); 36 | } else if (!isNaN(parseFloat(text)) && isFinite(text)) { 37 | for (var i = 0; i < logins.length; i++) { 38 | if (chat_id == logins[i]) { 39 | sBot.getRange("C" + (i + 2)).setValue(text); 40 | replaceKeyBoard(chat_id, sBot.getRange("D" + (i + 2)).getValue()); 41 | } 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Apps Script/BotTest/parseTextTest.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * MarkdownV2格式回复测试 3 | * 直接运行即可测试 testText 中文案是否符合TG发送标准 4 | * 正确:正确情况,个人Bot将私聊你并发送文案 5 | * 错误:错误情况将无反应 6 | */ 7 | 8 | let testText = `💊 *QX & Clash & TgBot 图文教程* 9 | 10 | 🌈 *[XiaoMao推文合集](http://mtw.so/5MH2zy)* 11 | 12 | *① 从入门到放弃* 13 | >*⒈ [入门:QX上手](http://mtw.so/5MH2Um)* 14 | >*⒉ [进阶:QX配置](http://mtw.so/5UdfZ3)* 15 | >*⒊ [进阶:QX分流](http://mtw.so/69fduD)* 16 | >*⒋ [进阶:QX重写](http://mtw.so/6gLqzk)* 17 | >*⒌ [番外:BoxJs和SubStore](http://mtw.so/6ohDnT)* 18 | >*⒍ [高阶:Task脚本制作](https://mp.weixin.qq.com/s/8c-tn6OaSGCVXUo2DIWiww)* 19 | >*⒎ [高阶:广告拦截(抓包)](https://mp.weixin.qq.com/s/B_zMFU6vsAeE_IKyLXddtA)* 20 | >*⒏ [高阶:会员解锁(抓包)](https://t.me/xiaomaoJT/876)* 21 | 22 | 23 | *② 垂死挣扎* 24 | >⒈ *[QX本地脚本使用教程](https://github.com/xiaomaoJT/QxScript/blob/main/COURSE.md#-%E6%9C%AC%E5%9C%B0%E8%84%9A%E6%9C%AC%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95)* 25 | >⒉ *[BoxJS使用教程](https://t.me/xiaomaoJT/951)* 26 | >⒊ *[Tg机器人搭建](https://github.com/xiaomaoJT/TgBot/blob/main/COURSE.md)* 27 | >⒋ *[Clash配置](https://github.com/xiaomaoJT/clash/raw/main/%E3%80%90%E5%B8%BD%E6%95%99%E7%A8%8B%E3%80%91Clash%E9%85%8D%E7%BD%AE%E6%95%99%E7%A8%8B.png?raw=true)* 28 | 29 | 30 | *欢迎点赞评论,感谢支持!*`; 31 | 32 | function myFunction() { 33 | let payload = { 34 | method: "sendMessage", 35 | chat_id: KingId, 36 | text: testText, 37 | parse_mode: "MarkdownV2", 38 | disable_web_page_preview: true, 39 | }; 40 | let data = { 41 | method: "post", 42 | payload: payload, 43 | }; 44 | const linkBot = (data) => { 45 | try { 46 | UrlFetchApp.fetch("https://api.telegram.org/bot" + BOTID + "/", data); 47 | } catch (error) {} 48 | }; 49 | linkBot(data); 50 | } 51 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@BetaVersion/MaoBotTest/maoBotKing.gs: -------------------------------------------------------------------------------- 1 | // 用于测试推送服务 2 | // 此代码仅用于测试 3 | 4 | var BOTID = ""; 5 | var KingId = ""; 6 | /** 7 | * 用于捕捉机器人信息 8 | * @param key 用户消息 9 | */ 10 | function pushDataToKing(key) { 11 | let userMessage = key; 12 | //用于捕捉机器人信息 13 | let messageToKing = 14 | "🧩 XiaoMaoBot捕捉到用户消息" + 15 | "\n" + 16 | "\n" + 17 | "📝 信息内容:" + 18 | userMessage.message.text + 19 | "\n" + 20 | "\n" + 21 | "🎎 信息发送人:" + 22 | (userMessage.message.from.first_name != undefined 23 | ? userMessage.message.from.first_name 24 | : "") + 25 | (userMessage.message.from.last_name != undefined 26 | ? userMessage.message.from.last_name 27 | : "") + 28 | "\n" + 29 | "\n" + 30 | "🛎 消息发送时间:" + 31 | getNowDate() + 32 | "\n" + 33 | "\n" + 34 | "📰 消息原始Json:" + 35 | "\n" + 36 | JSON.stringify(userMessage); 37 | let dataKing = { 38 | method: "post", 39 | payload: {}, 40 | }; 41 | dataKing.payload = { 42 | method: "sendMessage", 43 | chat_id: KingId, 44 | text: messageToKing, 45 | parse_mode: "HTML", 46 | disable_web_page_preview: true, 47 | }; 48 | UrlFetchApp.fetch("https://api.telegram.org/bot" + BOTID + "/", dataKing); 49 | } 50 | 51 | function getNowDate() { 52 | let date = new Date(); 53 | let sign2 = ":"; 54 | let year = date.getFullYear(); // 年 55 | let month = date.getMonth() + 1; // 月 56 | let day = date.getDate(); // 日 57 | let hour = date.getHours(); // 时 58 | let minutes = date.getMinutes(); // 分 59 | let seconds = date.getSeconds(); //秒 60 | let weekArr = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]; 61 | let week = weekArr[date.getDay()]; 62 | // 给一位数的数据前面加 “0” 63 | if (month >= 1 && month <= 9) { 64 | month = "0" + month; 65 | } 66 | if (day >= 0 && day <= 9) { 67 | day = "0" + day; 68 | } 69 | if (hour >= 0 && hour <= 9) { 70 | hour = "0" + hour; 71 | } 72 | if (minutes >= 0 && minutes <= 9) { 73 | minutes = "0" + minutes; 74 | } 75 | if (seconds >= 0 && seconds <= 9) { 76 | seconds = "0" + seconds; 77 | } 78 | return ( 79 | year + 80 | "/" + 81 | month + 82 | "/" + 83 | day + 84 | " " + 85 | hour + 86 | sign2 + 87 | minutes + 88 | sign2 + 89 | seconds 90 | ); 91 | } 92 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@BetaVersion/MaoBotTest/maoBotTest.gs: -------------------------------------------------------------------------------- 1 | // 用于测试api接口 2 | // 此代码仅用于测试 3 | 4 | // 机器人id 5 | var BOTID = ""; 6 | // tg chatid 7 | var KingId = ""; 8 | 9 | function getBotTest(word) { 10 | let responseHelloBot = null; 11 | let returnText = ""; 12 | 13 | try { 14 | responseHelloBot = UrlFetchApp.fetch("url"); 15 | let jsonData = JSON.parse(responseHelloBot.getContentText()); 16 | returnText = "以下数据由XiaoMao加工:" + "\n" + "\n" + ""; 17 | } catch (e) { 18 | returnText = 19 | "你的指令已成功发送,但由于运营商网络管制,本次通信被异常中止。"; 20 | } 21 | return returnText; 22 | } 23 | 24 | console.log(getBotTest("xxx")); 25 | 26 | let payload = { 27 | method: "sendMessage", 28 | chat_id: KingId, 29 | text: getBotTest("xxx"), 30 | parse_mode: "HTML", 31 | disable_web_page_preview: true, 32 | }; 33 | 34 | console.log(payload); 35 | 36 | let data = { 37 | method: "post", 38 | payload: payload, 39 | }; 40 | UrlFetchApp.fetch("https://api.telegram.org/bot" + BOTID + "/", data); 41 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@BetaVersion/README.md: -------------------------------------------------------------------------------- 1 | ### 🎟 XiaoMao 机器人脚本源码解析 2 | 3 | #### **测试版本 - 一体化部署 - 停止维护 ** 4 | 5 | **最新版本请见XiaoMaoBot正式版** 6 | 7 | > **作者@XiaoMao** 8 | 9 | > **小版本更新请查看更新日志 | 或加入 xiaomao 组织 ⬇️** 10 | > **微信公众号 【小帽集团】** 11 | > **XiaoMao · Tg 频道频道:https://t.me/xiaomaoJT** 12 | > 13 | > ------ 14 | > 15 | > **Google App Script** 16 | > **用于执行 tg 机器人功能** 17 | > 18 | > ------ 19 | > 20 | > **源码开发不易,使用引用请注明出处!** 21 | > 22 | > **[XiaoMao 机器人](https://t.me/Xiao_MaoMao_bot)** 23 | 24 | --- 25 | 26 | #### 🎟 **源码部分解析** 27 | 28 | > 可能需要 javascript 基础。 29 | 30 | > 本程序代码基于 Google Apps Script 环境实现,脱离环境将无法运行。 31 | 32 | > 【无法通过GAS直接执行问题】机器人通过检测到TG消息方才会响应,直接运行将使得入口函数doPost缺失关键参数而导致失败,若需直接执行,请于本地补全参数e,可作于调试运行。参数e的获取建议于部署完成后,通过私人推送服务获取原始数据。 33 | 34 | > 【Address unavailable 问题】导致您问题的原因可能是被阻止的 IP 地址。Google 使用不同的 IP 与服务器通信。有时,服务器会阻止一个或多个 Google 拥有的 IP 地址,从而导致地址不可用错误。每个请求使用不同的 IP 进行通信,因此当我们处理大量 urls 时,有可能会遇到阻塞的 IP 地址,从而导致地址不可用(大数定律)。该问题 Google 暂无解决方案,请更换接口,避免使用公开接口。 35 | 36 | > 【预设回复字数过多而造成无响应问题】[问题解析:本问题可能来源于会员限制,非TG会员单次可发送字数限制在1024字符内]当前逻辑,当预设的replyWord字段数量过长时,引用回复将出现无响应现象,建议排查过长的replyWord字段,采取分段方式处理。如【脚本合集】或【/js_vip】指令的处理方式以解决。 37 | 38 | > 【私人推送群聊来源捕获说明】群聊分为公开群组和私聊群组,公开群组才有username方可支持查看来源,私人群组仅可通过邀请链接进入,未获取邀请链接无法查看。 39 | 40 | 41 | --- 42 | 43 | #### 🎟 源码使用前准备 44 | 45 | > 教程详见首页 [简略配置教程](https://github.com/xiaomaoJT/TgBot#-tg机器人-简略配置教程) 46 | 47 | ```javascript 48 | // ==== 必填参数 ==== 49 | // ==== 必填参数 ==== 50 | // ==== 必填参数 ==== 51 | 52 | // 使用前请注意完善以下三个参数,即可部署运行。 53 | // Google EXEC ID - 谷歌表格ID 54 | var EXECID = ""; // ⚠️必填⚠️ 55 | // Google EXEC ID - 谷歌表格 工作表名 56 | var EXECNAME = "";// ⚠️必填⚠️ 57 | // Telegram BOT ID - tg机器人Token 58 | var BOTID = "";// ⚠️必填⚠️ 59 | 60 | 61 | 62 | // ==== 选填参数 - 个性化功能项 ==== 63 | // ==== 选填参数 - 个性化功能项 ==== 64 | // ==== 选填参数 - 个性化功能项 ==== 65 | 66 | // 用于推送主人消息 取主人tg id - 私人消息主动功能必须填写此项 67 | var KingId = ""; // ⚠️建议填写,留空影响推送⚠️ 68 | // 1 全部类型 69 | // 2 群聊 + 私聊类型 70 | // 3 私聊类型 71 | // 4 群聊类型 72 | // 5 关闭 73 | var KingType = 1; 74 | // 1 推送详情(原图片、视频、音频、贴纸等) 75 | // 0 仅推送基础消息 76 | var KingInfo = 1; 77 | //填写 bot id 用于识别引用消息 78 | var botIdAlone = ""; // ⚠️建议填写,留空影响引用类型消息⚠️ 79 | // 用于过滤需要排除捕捉的群组信息 80 | // 请填入群组id,多个用,间隔 如 ['22222','11111] 81 | var forGotList = []; 82 | // 权限释放 - 用于开放操作权限给管理员 83 | var PermissionRelease = true; 84 | // 管理员ID列表 如 ['11111','22222',KingId] 85 | var PermissionReleaseList = [KingId]; 86 | 87 | 88 | 89 | // ==== 默认参数 - 无需改动 ==== 90 | // ==== 默认参数 - 无需改动 ==== 91 | // ==== 默认参数 - 无需改动 ==== 92 | 93 | // 用于判断消息类型 - inlinekey board回调 or 主动消息 94 | // 1 callback 95 | // 2 new member 96 | // 3 left member 97 | var MESSAGETYPE = 0; 98 | //接入时间戳 弃用 99 | //var responseTime = ""; 100 | // 用于承接返回数据 101 | var dealMessage = {}; 102 | 103 | // ------------------------- 敏感词库 ------------------------- 104 | 105 | // 强ban关键字截止位 - 即取前八个,触发即ban 106 | var banKeyLastIndex = 8; 107 | // 见下文checkSensitiveDFA方法介绍 108 | var sensitiveEncodeList = [...] 109 | ``` 110 | 111 | --- 112 | 113 | #### 🎟 源码方法与参数解析 114 | 115 | ##### **🎨 doPost**方法 116 | 117 | > 接收参数 e 为 bot 回传的响应参数,字符串格式的 json 串。 118 | > 119 | > 用于接收用户传来的讯息并进行处理 120 | 121 | ```text 122 | 该函数为主函数,当代码部署并与bot建立连接后,将在每次响应后自动调用该方法。 123 | 主功能为接收用户讯息,处理用户讯息,回传讯息。 124 | 125 | 回传时处理:为避免频繁响应,仅对指定关键字、私聊、@类型消息进行回传调用。 126 | 127 | 注意项:GAS首次部署将会有默认函数,请注意将doPost函数修改为入口函数。 128 | ``` 129 | 130 | --- 131 | 132 | ------ 133 | 134 | ##### **🎨 processData**方法 135 | 136 | > 用于定义自定义键盘 及 处理后的用户回复文本格式参数 137 | > 138 | > 用于判断消息类型及处理用户回复文本格式参数 139 | > 140 | > 调用函数对消息进行存储 141 | 142 | ```text 143 | 该函数主要用于配置自定义键盘及格式化回传参数。 144 | 自定义键盘分底部键盘及内联键盘两种,可针对不同消息类型进行个性化匹配。 145 | 146 | 针对用户讯息进行判断,对多类型进行不同处理或进一步处理,可包含文本、图片、语言、回调、视频等类型。 147 | ``` 148 | 149 | ##### **🎳 followKeyboard** 参数 聊天窗口底部自定义键盘 150 | 151 | > 点击效果为发送预设文字。例如 text: "公众号小帽集团",则点击自动发送公众号小帽集团 152 | > 153 | > 可通过 keyboardParams 参数进行键盘设定,具体可参考:https://core.telegram.org/bots/api#replykeyboardmarkup 154 | 155 | ##### **🎳 followMessageKeyboard** 参数 消息跟随在线键盘 156 | 157 | > 样式为跟随在回复的消息底部,可通过特定参数进行激活 158 | > 159 | > 一般有两种样式,一是 url 链接形式,点击可实现跳转指定链接;二是 callback 形式,点击进行回调,可执行指定操作。 160 | > 161 | > 可通过 keyboardFollowParams 参数进行键盘设定,具体可参考:https://core.telegram.org/bots/api#inlinekeyboardmarkup 162 | 163 | ```text 164 | 回调参数,经测试Google Apps Script无法直接通过e.postData.contents.callback_query在其他函数内进行判定,故通过doPost函数的MESSAGETYPE参数进行预判定。 165 | 166 | 判定成功后,激活回调,将收到callback_data预定内容,可根据该内容进行指定回调操作。 167 | ``` 168 | 169 | --- 170 | 171 | ------ 172 | 173 | ##### 🎨 **processReplyWord**方法 174 | 175 | > 用于处理用户信息并进行回复文本处理 176 | 177 | > [replyWord 参数,针对 html 格式文本回复](https://core.telegram.org/bots/api#formatting-options) 178 | 179 | ```javascript 180 | // 官方目前仅兼容以下标签 181 | bold, bold 182 | italic, italic 183 | underline, underline 184 | strikethrough, strikethrough, strikethrough 185 | spoiler, spoiler 186 | bold italic bold italic bold strikethrough italic bold strikethrough spoiler underline italic bold bold 187 | inline URL 188 | inline mention of a user 189 | inline fixed-width code 190 |
pre-formatted fixed-width code block
191 |
pre-formatted fixed-width code block written in the Python programming language
192 | ``` 193 | 194 | ```text 195 | # 显示效果 196 | *bold \*text* 197 | _italic \*text_ 198 | __underline__ 199 | ~strikethrough~ 200 | ||spoiler|| 201 | *bold _italic bold ~italic bold strikethrough ||italic bold strikethrough spoiler||~ __underline italic bold___ bold* 202 | [inline URL](http://www.example.com/) 203 | [inline mention of a user](tg://user?id=123456789) 204 | `inline fixed-width code` 205 | ``` 206 | 207 | pre-formatted fixed-width code block 208 | ```` 209 | ```python 210 | pre-formatted fixed-width code block written in the Python programming language 211 | ```` 212 | ``` 213 | 214 | ##### **🎳 autoReply** 参数 自定义关键字匹配 215 | 216 | > **keyword** 参数为某规则关键字列表,请注意使用数组[]格式,请注意关键字不要重复。 217 | > 218 | > **replyWord** 参数为某关键字所对应的回复语句,html 格式或字符串格式,具体规则可参考:https://core.telegram.org/bots/api#formatting-options。 219 | > 220 | > **"\n"**为换行符 221 | > 222 | > **htmlReply** 参数为默认回复语句 223 | > 224 | > **outsideWord** 参数为关键字排除,将优先排除内定的关键字匹配 225 | 226 | ##### 🎳 **commandWord** 参数 api 指令参数匹配 227 | 228 | > 针对 api 接口,进行指定参数指令匹配,将自动匹配设定的 api 接口,函数会自动处理,提取指令后面的查询语句 229 | > 230 | > 接口数据来源于随身助手 API,无需申请 token,但可能存在网络拥挤情况,可稍后再试! 231 | 232 | ##### 🎳 **returnHtmlReply** 参数 回复状态确认 233 | 234 | > 仅针对关键字类型进行回复确认,其余类型默认不回复。优先级低于 doPost 方法。 235 | 236 | ------ 237 | 238 | ------ 239 | 240 | ##### 🎨 getRelayTime方法 241 | 242 | > 响应延迟计算方法 243 | > 244 | > 针对每次机器人响应,进行延迟时间计算 245 | 246 | ------ 247 | 248 | ------ 249 | 250 | ##### 🎨 **getString**方法 251 | 252 | > 用于截取 api 关键字后查询语句 253 | 254 | --- 255 | 256 | ------ 257 | 258 | ##### 🎨 **isApi**方法 259 | 260 | > 用于判断是否为 api 关键字 261 | 262 | --- 263 | 264 | ------ 265 | 266 | ##### 🎨 Api 查询方法 267 | 268 | > - getVideo 视频查询 269 | > - getDuJiTang *毒鸡汤*查询 270 | > - getTianGou _舔狗日记生成_ 271 | > - getPhoneWhere 查询手机号码归属地 272 | > - getYiYan _一言查询_ 273 | > - getMusic _随机歌曲_ 274 | > - getLinkShort 短网址生成 275 | > - getWeatherApi 天气 api 查询 276 | > - getHelloBot 聊天机器人 277 | > - getCOVID19 *全国*疫情查询 功能已下架 278 | > - getMiSport 小米运动刷步 279 | > - getLanLink *蓝奏云直链解析* 280 | > - getChatBot chatGPT 281 | > - getHotList 热榜查询 282 | > - getHoroscopeList 星座运势 283 | > - getDouBan 豆瓣电影排行 284 | 285 | --- 286 | 287 | ------ 288 | 289 | ##### 🎨 **setStorage**方法 290 | 291 | > 用于存储用户讯息 292 | 293 | --- 294 | 295 | ------ 296 | 297 | ##### 🎨 **getNowDate**方法 298 | 299 | > 用于格式化日期对象 300 | 301 | ------ 302 | 303 | ------ 304 | 305 | ##### 🎨 **checkSensitiveDFA**方法 306 | 307 | > 基于 dfa 算法的关键字过滤,用于过滤敏感词 308 | > 309 | > 敏感词**sensitiveEncodeList**使用 base64 加密 310 | > 311 | > ⚠️sensitiveEncodeList列表自{@Beta4.6-619}版本后提前至函数顶部位置 312 | > 313 | > 默认取sensitiveEncodeList前七位作为绝杀关键字,即触发立即拉黑、封禁、踢群、删消息。 314 | 315 | ------ 316 | 317 | ------ 318 | 319 | 320 | ##### 🎨 **pushDataToKing**方法 321 | 322 | > 用于捕捉消息并对主人进行推送 323 | > 324 | > 通过 **KingId & KingType** 控制 325 | > 326 | > 需填写KindId后此方法才能生效 327 | 328 | ------ 329 | 330 | ------ 331 | 332 | ##### 🎨 getReply方法 333 | 334 | > 用于主人对私聊信息进行bot角色回复 335 | > 336 | > **KindId**必填,此方法方可生效 337 | > 338 | > 此功能依赖于**pushDataToKing**推送服务 339 | > 340 | > 私有指令【/reply+内容】 341 | 342 | ##### 🎨 getBanUser方法 343 | 344 | > *封禁用户* 345 | > 346 | > **KindId**必填,此方法方可生效 347 | > 348 | > 此功能依赖于**pushDataToKing**推送服务 349 | > 350 | > 封禁时长分为三种(1、N分钟:Nm 如30分钟:30m ;2、N天:Nd 如30天:30d ;3、不填:永久封禁) 351 | > 352 | > 私有指令【/ban + 时长】 353 | 354 | ##### 🎨 getUnBanUser方法 355 | 356 | > 解封用户 357 | > 358 | > **KindId**必填,此方法方可生效 359 | > 360 | > 此功能依赖于**pushDataToKing**推送服务 361 | > 362 | > 私有指令【/unban】 363 | 364 | ##### 🎨 getRestrictUser方法 365 | 366 | > *限制用户权限* 367 | > 368 | > **KindId**必填,此方法方可生效 369 | > 370 | > 此功能依赖于**pushDataToKing**推送服务 371 | > 372 | > 封禁时长分为三种(1、N分钟:Nm 如30分钟:30m ;2、N天:Nd 如30天:30d ;3、不填:永久封禁) 373 | > 374 | > 私有指令【/restrict + 时长】 375 | 376 | 🎨 **getUnixTime方法** 377 | 378 | > *获取unix时间戳* -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/MaoBot@OfficialVersion/.DS_Store -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/COURSE.md: -------------------------------------------------------------------------------- 1 | **[TgBot](https://github.com/xiaomaoJT/TgBot)** ***https://github.com/xiaomaoJT/TgBot*** **@XiaoMao** 2 | 3 | **[<< 回到首页](https://github.com/xiaomaoJT/TgBot)** 4 | 5 | 6 | 7 | ```text 8 | * author : @XiaoMao 9 | * xiaomao组织⬇️ 10 | * # 微信公众号 【小帽集团】 11 | * # Tg频道频道:https://t.me/xiaomaoJT 12 | * 13 | * 正式版 V1.00 - updateTime@20240805 14 | 15 | * 16 | * 源码开发不易,使用引用请注明出处! 17 | * 源码开发不易,使用引用请注明出处! 18 | * 源码开发不易,使用引用请注明出处! 19 | * 20 | 21 | 正式版特点: 22 | 1、代码模块化划分 23 | 2、数据表独立管理 24 | 3、配置独立管理 25 | 26 | 功能描述: 27 | ❶ 超级群管功能 28 | ❷ 广告词/敏感词过滤、自动删除/警告 29 | ❸ 多样化接口查询、数据加工 30 | ❹ 自定义聊天窗快捷键盘/消息跟随按钮 31 | ❺ 关键字消息/私聊消息 自定义配置|自动回复 32 | ❻ 私聊消息/群组消息 捕捉及消息私人推送 33 | ❼ 私聊消息/群组消息 自动存储 34 | 35 | 功能细则: 36 | 入群检测|退群检测 37 | 入群欢迎|退群欢送 38 | 超级群管功能:用户封禁、用户解封、用户禁言 39 | 广告词敏感词拦截及自动删除 40 | chatGPT查询 41 | 消息私人推送 42 | BOT消息主动回复 43 | 自动接口查询及数据加工 44 | 自定义键盘 45 | 私聊及自动回复 46 | 关键字自动回复 47 | 消息存储 48 | 等 49 | 50 | 51 | ``` 52 | 53 | 54 | 55 | ------------ 56 | 57 | #### 🎟 Telegram Bot 快速使用教程 58 | 59 | > 1. 关注并激活[XiaoMaoBot机器人](https://t.me/Xiao_MaoMao_bot)。 60 | > 2. 将XiaoMaoBot机器人拉入群聊。 61 | > 3. 授予XiaoMaoBot机器人管理员权限。 62 | > 4. 至此即可自动开启XiaoMaoBot机器人所有功能。 63 | 64 | ------ 65 | 66 | ------ 67 | 68 | #### 🎟 Telegram Bot 快速搭建教程 69 | 70 | ##### 视频教程 71 | 72 | ▶ [正式版部署 - 视频教程](https://www.alipan.com/s/dW2yPirBysi) ✅ 73 | 74 | 75 | 76 | ##### 文字教程 77 | 78 | ###### 🚗 第一步:创建\登陆谷歌账号,并打开[谷歌云端硬盘](https://drive.google.com/drive/my-drive?ths=true) 79 | 80 | ```text 81 | 1⃣️ Google账号请自行创建,此处不作详细说明~ 82 | 2⃣️ 登陆Google账号,并打开Google Drive 83 | 3⃣️ 谷歌云端硬盘地址:https://drive.google.com/drive/my-drive?ths=true 84 | ``` 85 | 86 | 87 | ###### 🚗 第二步:创建个人tg机器人 88 | 89 | ```text 90 | 1⃣️ telegram搜索 @BotFather 机器人 91 | 2⃣️ 点击 @BotFather 菜单 /newbot 或 自行输入 92 | 3⃣️ 待机器人响应后 按要求输入 自己的机器人名称,注意名称要求小写字母,且以bot结尾,例如maobot或者mao_bot 等等 93 | 4⃣️ 如果提示被使用,则继续重新输入新名称,直到可用为止 94 | 5⃣️ 创建成功后,将自动生成一串token代码,即为你的机器人ID码 95 | 6⃣️ 通过 @BotFather 菜单 /setjoingroups ,选择机器人后,点击Enable按钮打开机器人加群功能,开启后可通过机器人详情页,实现新增到群组或频道功能。 96 | 7⃣️ 至此机器人创建完成。 97 | ``` 98 | 99 | ```text 100 | ⚠️ 注意事项: 101 | 1⃣️ 机器人token很重要,需记下来❕ 102 | 🚨用于「var BOTID = ""」中使用 103 | 104 | 2⃣️ 如果泄漏,可以通过@BotFather进行重置。 105 | 3⃣️ 机器人可通过 @BotFather 发送 /mybots 指令进行类似描述、头像、关于、指令等基本设定,这里不做深究 106 | 107 | 更多教程可查看:https://ithelp.ithome.com.tw/articles/10245264 108 | ``` 109 | 110 | 111 | ###### 🚗 第三步:新建 Google 表格 112 | 113 | ```text 114 | 1⃣️ 左上角新建,选择Google 表格即可。 115 | 2⃣️ 浏览器地址栏,记下表格ID 116 | ``` 117 | 118 | ```javascript 119 | //例如 120 | https://docs.google.com/spreadsheets/d/XXXXXXX这一串就是表格IDXXXXXXXXXXX/edit#gid=0 121 | ``` 122 | 123 | ```text 124 | 3⃣️ 导入初始化表 125 | 4⃣️ 至此,表格新建完毕。 126 | ``` 127 | 128 | ```text 129 | ⚠️ 注意事项: 130 | 1⃣️ 表格ID很重要,需记下来❕ 131 | 🚨用于「var EXECID = ""」中使用 132 | 133 | 2⃣️ 初始化表格下载地址 134 | https://raw.githubusercontent.com/xiaomaoJT/TgBot/main/Apps%20Script/MaoBot%40OfficialVersion/DB/XiaoMaoBot_DB.xlsx 135 | ``` 136 | 137 | ###### 🚗 第四步:创建Google Apps Script函数,完成机器人部署 138 | 139 | ```text 140 | 1⃣️ 打开刚刚创建的Google表格,点击工具栏 扩展程序 > Apps脚本 141 | 2⃣️ 按「Modules」目录新建gs脚本「Maobot、Params、Core、Manage、Api、Utils」 142 | https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script/MaoBot%40OfficialVersion/Modules 143 | 【⚠️根据以上文件创建,请注意『MaoBot.gs』需放首位,请注意删除网页上自带的所有代码!】 144 | 145 | 3⃣️ 完善「params.gs」中的参数「EXECID」、「BOTID」 146 | 4⃣️ 私聊XiaoMaoBot,回复/myid ,获取个人ID,完善「params.gs」中参数「KingId」 147 | https://t.me/Xiao_MaoMao_bot 148 | 149 | 5⃣️ 完善「params.gs」中的参数「botIdAlone」 150 | 56698908989:xxxxxxxx_xxxxxxxx_xxxxxxxxxxxx 151 | 其中:前面「56698908989」即是「botIdAlone」 152 | 153 | 6⃣️ ⚠️保存代码,文件名可随意自定义【⚠️必须手动保存一次,注意顶部myfunction函数变为doPost函数,未切换请手动切换doPost】 154 | 155 | 7⃣️ 点击部署 > 新建部署 156 | 8⃣️ 选择部署类型 > web应用 157 | 9⃣️ 描述自行随意填写,有访问权限的人员 选择 任何人 158 | 🔟点击部署,进行全部授权,全部允许(左下角小字点开,选择展开后最下面的按钮) 159 | 1⃣️1⃣️ 部署成功后,复制web应用网址,替换下面链接,并复制到浏览器打开进行机器人激活 160 | 1⃣️2⃣️ 完成,可以通过群聊或者私聊自己的机器人进行消息回复了,并且交互的信息将会自动存于Google表格中。 161 | 1⃣️3⃣️ 群聊请注意授予机器人管理权限,部分功能激活请根据源码参数解析提示补充完整必要参数。 162 | ``` 163 | 164 | ```javascript 165 | https://api.telegram.org/bot 你的tg机器人Token /setWebhook?url=你的web应用网址 166 | //上面链接替换 你的tg机器人Token和你的web应用网址 内容即可 167 | //⚠️注意链接不要留空格 168 | //⚠️注意链接不要留空格 -- 空格会被浏览器自动转义成 %20 ,请注意排查问题~ 169 | //⚠️注意链接不要留空格 170 | //注意token前面有bot需要保留 171 | 172 | //替换完成后,复制链接到浏览器打开,返回下方内容皆为成功 173 | {"ok":true,"result":true,"description":"Webhook is set"} 174 | {"ok":true,"result":true,"description":"Webhook is already set"} 175 | 176 | //返回下面内容,则tg机器人Token失效,请通过 @BotFather 进行重置,然后再次执行第四步 重新部署 177 | {"ok":false,"error_code":401,"description":"Unauthorized"} 178 | 179 | 源码开发不易,使用引用请注明出处!遇到问题欢迎留言~ 180 | 181 | ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️ 182 | 183 | 请注意,以上激活状态仅代表gas WEB部署完成,并不代表可完整运行。 184 | 185 | 完整功能激活请注意检查以下几点: 186 | 1、必要参数填写完整 ⚠️ 187 | 2、私聊检测【私聊机器人 所有能力表现完整✅】 188 | 3、群聊内赋予机器人管理权限。 189 | 4、google表格内容会随群聊或机器人私聊自动捕捉信息并实时写入⚠️【私聊表格必须有新数据写人✅】 190 | 191 | 🚨代码错误排查 192 | 1、打开Google Apps Script 193 | 2、左侧菜单打开 脚本执行 194 | 3、查看允许日志及报错提示 195 | 4、根据提示再问题追踪 196 | ``` 197 | 198 | ------ 199 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/DB/COURSE.md: -------------------------------------------------------------------------------- 1 | **[TgBot](https://github.com/xiaomaoJT/TgBot)** ***https://github.com/xiaomaoJT/TgBot*** **@XiaoMao** 2 | 3 | **[<< 回到首页](https://github.com/xiaomaoJT/TgBot)** 4 | 5 | 6 | 7 | ##### 🌠 DB数据库 V1.20+ 8 | 9 | ```text 10 | 初始化数据表 11 | 1、新建Google表格 12 | 2、选择「文件」-「导入」-「上传本地文件XiaoMaoBot_DB.xlsx」-「替换电子表格」-「导入数据」 13 | 14 | 数据表结构介绍 15 | · db_telegram -- 主体储存表 16 | 「无需操作,自动写入」 17 | 18 | · key_params -- 关键字配置表 19 | 「自动回复关键字内容,按表头要求填写,支持无限多个,支持多段文本持续回复」 20 | 「⚠️数据缓存默认3小时刷新一次」 21 | 22 | · authority_management -- 权限控制表 23 | 「群组消息屏蔽、管理员权限释放」 24 | ``` 25 | 26 | 27 | 28 | #### 🎟 db_telegram - 主体储存表 29 | 30 | > 用于存储数据,无需操作,自动写入 31 | 32 | > 建议定期清理,避免因部分查询操作反复读表而造成BOT性能损失 33 | 34 | ##### 🧩 基础表结构 35 | 36 | | 发起时间 | 用户ID | 用户名称 | 用户昵称 | 消息类型 | 消息来源 | 来源ID | 消息内容 | 消息JSON | 37 | | -------- | ------ | -------- | -------- | -------- | -------- | ------ | -------- | -------- | 38 | 39 | 40 | 41 | #### 🎟 key_params - 关键字配置表 42 | 43 | ##### 🧩 基础表结构 44 | 45 | | 关键字块 | 标识块 | 内容块 | | 46 | | -------- | ------ | ------- | ---------- | 47 | | 关键字 | 标识块 | 内容块1 | 内容块2... | 48 | 49 | > 关键字回复列表按「行」为单位进行记录,根据标识块对关键字进行回复内容块文本 50 | 51 | > 为避免无效响应,建议删除多余空白行,「右键选中 - 删除行」 52 | 53 | ##### 🎲 关键字 - 必填 54 | 55 | > 关键字列,支持多关键字,以英文逗号「 , 」来间隔。 56 | 57 | > 如:懒人规则,懒人配置 58 | 59 | ##### 🎲 标识块 - 必填 60 | 61 | > 标识块列,当前「V1.20」版本以上支持「HTML」、「MarkdownV2」、「GraphicMessage」、「VideoMessage」模式。 62 | 63 | ``` 64 | 📋HTML 65 | HTML格式,标签仅支持官方示例内标签,非官方示例标签可能出现回复失败问题。【官方示例:https://core.telegram.org/bots/api#html-style】 66 | 使用HTML格式,需在标识块指定填写「HTML」以激活 67 | 请注意特殊转换, 标签将会自动转换为可点击链接,不可再嵌套其他标签 68 | ⚠️内容块文本请严格按照官方「HTML」格式要求进行填写,填写逻辑错误将无法响应!! 69 | 70 | 📋MarkdownV2 71 | MarkdownV2格式【官方示例:https://core.telegram.org/bots/api#markdownv2-style】 72 | 使用MarkdownV2格式,需在标识块指定填写「MarkdownV2」以激活 73 | ⚠️内容块文本请严格按照官方「MarkdownV2」格式要求进行填写,填写逻辑错误将无法响应!! 74 | 75 | 76 | 📋GraphicMessage 77 | 使用GraphicMessage格式,需在标识块指定填写「GraphicMessage」以激活 78 | 内容块1填写图片文件ID或图片URL,多条按「换行」填写 79 | 内容块2填写需要跟随的文本内容,暂仅支持「MarkdownV2」格式 80 | 🚨 图片ID查询(上传图片并附带文字指令以激活) 「 #photoid 」 81 | ⚠️ 官方建议以ID方式调用,性能更佳 82 | 83 | 📋VideoMessage 84 | 使用VideoMessage格式,需在标识块指定填写「VideoMessage」以激活 85 | 内容块1填写视频文件ID或视频URL,多条按「换行」填写 86 | 内容块2填写需要跟随的文本内容,暂仅支持「MarkdownV2」格式 87 | 🚨视频ID查询(上传视频并附带文字指令以激活)「 #videoid 」 88 | ⚠️ 官方建议以ID方式调用,性能更佳 89 | ``` 90 | 91 | ###### 🎲 内容块 - 必填 92 | 93 | **[文本格式响应逻辑测试代码](https://raw.githubusercontent.com/xiaomaoJT/TgBot/main/Apps%20Script/BotTest/parseTextTest.gs)** 94 | 95 | > 理论上,内容块支持无限多个,不同内容块将分开消息体连续发送,建议单内容块文本字符数控制在1024字以内 96 | 97 | > ⚠️内容块文本请严格按照官方格式要求进行填写,填写逻辑错误将无法响应!! 98 | 99 | 100 | 101 | ##### 🎟authority_management - 权限控制表 102 | 103 | > 理论上,IDs支持无限多个,均为全数字 104 | 105 | > 群组ID带有 - 号 106 | > 107 | > 管理员ID可私聊@Xiao_MaoMao_bot 回复【/myid】获取 108 | 109 | ##### 🧩 基础表结构 110 | 111 | | 类型 | IDs | | 112 | | ------------ | ---- | ---- | 113 | | 群组屏蔽列表 | -111 | -222 | 114 | | 管理员列表 | 111 | 222 | 115 | 116 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/DB/XiaoMaoBot_DB.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/MaoBot@OfficialVersion/DB/XiaoMaoBot_DB.xlsx -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/Api.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 接口类 3 | * 4 | * 无需改动 5 | */ 6 | 7 | /** 8 | * 用于接口前的回复 9 | */ 10 | const apiReply = (id, useJson) => { 11 | let payloadPostData = { 12 | method: "sendMessage", 13 | chat_id: id, 14 | text: 15 | "🕹 来自XiaoMaoBot的消息:" + 16 | "\n" + 17 | "\n" + 18 | "您的查询指令已成功发送,本次查询过程中将受到运营商网络管制,若200s内无响应则此次通信将被异常终止,请稍后再试~", 19 | reply_to_message_id: useJson.message_id, 20 | parse_mode: "HTML", 21 | reply_markup: JSON.stringify(keyboardFollowParams), 22 | disable_web_page_preview: true, 23 | }; 24 | linkBot({ 25 | method: "post", 26 | payload: payloadPostData, 27 | }); 28 | }; 29 | 30 | /** 31 | * 骚话大全 ✅ 32 | * @param 33 | * @returns 34 | */ 35 | const getSao = () => { 36 | let responseSao = null; 37 | let returnText = 38 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 39 | 40 | try { 41 | responseSao = UrlFetchApp.fetch( 42 | "https://api.vvhan.com/api/text/sexy" + "×=" + new Date().getTime(), 43 | { 44 | muteHttpExceptions: true, 45 | } 46 | ); 47 | returnText = 48 | "以下数据来自韩小韩,由XiaoMao加工:" + 49 | "\n" + 50 | "\n" + 51 | responseSao.getContentText(); 52 | } catch (e) { 53 | return returnText; 54 | } 55 | return returnText; 56 | }; 57 | /** 58 | * 蓝奏云直链解析 ✅ 59 | * @param link 60 | * @returns 61 | */ 62 | const getLanLink = (link) => { 63 | let responseLink = null; 64 | let returnText = 65 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 66 | 67 | try { 68 | responseLink = UrlFetchApp.fetch( 69 | "https://apis.jxcxin.cn/api/lanzou?url=" + 70 | link + 71 | "×=" + 72 | new Date().getTime(), 73 | { 74 | muteHttpExceptions: true, 75 | } 76 | ); 77 | let jsonData = JSON.parse(responseLink.getContentText()); 78 | returnText = 79 | "以下数据来自API Store,由XiaoMao加工:" + 80 | "\n" + 81 | "\n" + 82 | "解析结果:" + 83 | (jsonData.code != 200 84 | ? jsonData.msg 85 | : jsonData.msg + 86 | "\n" + 87 | "\n" + 88 | "资源名称:" + 89 | jsonData.data.name + 90 | "\n" + 91 | "资源作者:" + 92 | jsonData.data.author + 93 | "\n" + 94 | "资源大小:" + 95 | jsonData.data.size + 96 | "\n" + 97 | "资源描述:" + 98 | jsonData.data.describe + 99 | "\n" + 100 | "资源直链地址:" + 101 | jsonData.data.url) + 102 | "\n"; 103 | } catch (e) { 104 | return returnText; 105 | } 106 | return returnText; 107 | }; 108 | /** 109 | * chat api✅ 110 | * @param word 111 | * @returns 112 | */ 113 | const getChatBot = (word) => { 114 | let responseHelloBot = null; 115 | let returnText = 116 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 117 | 118 | if (word == "") { 119 | returnText = "查询的内容为空,请在指令后面加上问题再试吧~"; 120 | return returnText; 121 | } 122 | 123 | try { 124 | responseHelloBot = UrlFetchApp.fetch( 125 | "https://v1.apigpt.cn/?q=" + 126 | word + 127 | "&apitype=sql×=" + 128 | new Date().getTime(), 129 | { 130 | muteHttpExceptions: true, 131 | } 132 | ); 133 | let jsonData = JSON.parse(responseHelloBot.getContentText()); 134 | returnText = 135 | "以下数据来自OpenAI&夏柔,由XiaoMao加工:" + 136 | "\n" + 137 | "\n" + 138 | "
" +
 139 |       jsonData.ChatGPT_Answer.toString()
 140 |         .replace("\n\n", "")
 141 |         .replace(/&/g, "&")
 142 |         .replace(//g, ">") +
 144 |       "
"; 145 | } catch (e) { 146 | return returnText; 147 | } 148 | return returnText; 149 | }; 150 | /** 151 | * 聊天api✅ 152 | * @param word 153 | * @returns 154 | */ 155 | const getHelloBot = (word) => { 156 | let responseHelloBot = null; 157 | let returnText = 158 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 159 | 160 | if (word == "") { 161 | returnText = "查询的内容为空,请在指令后面加上问题再试吧~"; 162 | return returnText; 163 | } 164 | 165 | try { 166 | responseHelloBot = UrlFetchApp.fetch( 167 | "http://api.qingyunke.com/api.php?key=free&appid=0&msg=" + 168 | word + 169 | "×=" + 170 | new Date().getTime(), 171 | { 172 | muteHttpExceptions: true, 173 | } 174 | ); 175 | let jsonData = JSON.parse(responseHelloBot.getContentText()); 176 | returnText = 177 | "以下数据来自菲菲,由XiaoMao加工:" + 178 | "\n" + 179 | "\n" + 180 | jsonData.content; 181 | } catch (e) { 182 | return returnText; 183 | } 184 | return returnText; 185 | }; 186 | /** 187 | * 视频查询 188 | * @param video 189 | * @returns 190 | */ 191 | const getVideo = () => { 192 | let returnText = ""; 193 | let url = 194 | "http://tucdn.wpon.cn/api-girl/index.php?wpon=" + 195 | parseInt(Math.random() * 99999); 196 | returnText = 197 | "以下数据来自wpon,由XiaoMao加工:" + 198 | "\n" + 199 | "\n" + 200 | "
美女小姐姐视频·点击在线播放" + 203 | "\n"; 204 | 205 | return returnText; 206 | }; 207 | /** 208 | * 毒鸡汤查询 209 | * @param music 210 | * @returns 211 | */ 212 | const getDuJiTang = () => { 213 | let responseDuJiTang = null; 214 | let returnText = 215 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 216 | 217 | try { 218 | responseDuJiTang = UrlFetchApp.fetch("https://api.btstu.cn/yan/api.php", { 219 | muteHttpExceptions: true, 220 | }); 221 | 222 | returnText = 223 | "以下数据来自博天,由XiaoMao加工:" + 224 | "\n" + 225 | "\n" + 226 | responseDuJiTang.getContentText(); 227 | } catch (e) { 228 | return returnText; 229 | } 230 | return returnText; 231 | }; 232 | /** 233 | * 舔狗日记生成 ✅ 234 | * @param id 235 | * @returns 236 | */ 237 | const getTianGou = () => { 238 | let responseTianGou = null; 239 | let returnText = 240 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 241 | // return returnText; 242 | try { 243 | responseTianGou = UrlFetchApp.fetch( 244 | "https://cloud.qqshabi.cn/api/tiangou/api.php", 245 | { 246 | muteHttpExceptions: true, 247 | } 248 | ); 249 | 250 | returnText = 251 | "以下数据来自God,由XiaoMao加工:" + 252 | "\n" + 253 | "\n" + 254 | responseTianGou.getContentText(); 255 | } catch (e) { 256 | return returnText; 257 | } 258 | return returnText; 259 | }; 260 | /** 261 | * 一言查询 ✅ 262 | * @returns 263 | */ 264 | const getYiYan = () => { 265 | let responseYiYan = null; 266 | let returnText = 267 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 268 | 269 | try { 270 | responseYiYan = UrlFetchApp.fetch( 271 | "https://apis.jxcxin.cn/api/yiyan?type=json×=" + 272 | new Date().getTime(), 273 | { 274 | muteHttpExceptions: true, 275 | followRedirects: true, 276 | validateHttpsCertificates: false, 277 | } 278 | ); 279 | if (200 == responseYiYan.getResponseCode()) { 280 | let jsonData = JSON.parse(responseYiYan.getContentText()); 281 | returnText = 282 | "以下数据来自API Store,由XiaoMao加工:" + 283 | "\n" + 284 | "\n" + 285 | jsonData.msg; 286 | } 287 | } catch (e) { 288 | return returnText; 289 | } 290 | 291 | return returnText; 292 | }; 293 | /** 294 | * 查询手机号码归属地✅ 295 | * @param phone 296 | * @returns 297 | */ 298 | const getPhoneWhere = (phone) => { 299 | let responsePhone = null; 300 | let returnText = 301 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 302 | 303 | if (phone == "") { 304 | returnText = "查询的手机号为空,请在指令后面加上手机号码再试~"; 305 | return returnText; 306 | } 307 | 308 | try { 309 | responsePhone = UrlFetchApp.fetch( 310 | "https://www.mxnzp.com/api/mobile_location/aim_mobile?mobile=" + 311 | phone + 312 | "&app_id=rgihdrm0kslojqvm&app_secret=WnhrK251TWlUUThqaVFWbG5OeGQwdz09" + 313 | "×=" + 314 | new Date().getTime(), 315 | { 316 | muteHttpExceptions: true, 317 | } 318 | ); 319 | 320 | let jsonData = JSON.parse(responsePhone.getContentText()); 321 | 322 | returnText = 323 | "以下数据来自Roll,由XiaoMao加工:" + 324 | "\n" + 325 | "\n" + 326 | "手机号码:" + 327 | jsonData.data.mobile + 328 | "\n" + 329 | "归属地:" + 330 | jsonData.data.province + 331 | "\n" + 332 | "运营商:" + 333 | jsonData.data.carrier; 334 | } catch (e) { 335 | return returnText; 336 | } 337 | return returnText; 338 | }; 339 | /** 340 | * 随机歌曲 ✅ 341 | * @param text 342 | * @returns 343 | */ 344 | const getMusic = () => { 345 | let responseMusic = null; 346 | let returnText = 347 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 348 | 349 | try { 350 | responseMusic = UrlFetchApp.fetch( 351 | "https://anime-music.jijidown.com/api/v2/music", 352 | { 353 | muteHttpExceptions: true, 354 | } 355 | ); 356 | let jsonData = JSON.parse(responseMusic.getContentText()); 357 | returnText = 358 | "以下数据来自Anime,由XiaoMao加工:" + 359 | "\n" + 360 | "\n" + 361 | "歌名:" + 362 | jsonData.res.anime_info.title + 363 | "\n" + 364 | "\n" + 365 | "歌手:" + 366 | jsonData.res.author + 367 | "\n" + 368 | "\n" + 369 | "简介:" + 370 | jsonData.res.anime_info.desc + 371 | "\n" + 372 | "\n" + 373 | "点击在线播放" + 376 | "\n"; 377 | } catch (e) { 378 | return returnText; 379 | } 380 | return returnText; 381 | }; 382 | /** 383 | * 短网址生成✅ 384 | * @param link 385 | * @returns 386 | */ 387 | const getLinkShort = (link) => { 388 | let responseLinkShort = null; 389 | let returnText = 390 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 391 | 392 | if (link == "") { 393 | returnText = "查询的内容为空,请在指令后面加上链接再试吧~"; 394 | return returnText; 395 | } 396 | 397 | try { 398 | let data = { 399 | url: link, 400 | token: "18a709553844b10c078c91bde2ec624f", 401 | mark: "来自pc网页", 402 | env_code: "self", 403 | times: new Date().getTime(), 404 | muteHttpExceptions: true, 405 | }; 406 | let option = { 407 | method: "post", 408 | payload: JSON.stringify(data), 409 | }; 410 | responseLinkShort = UrlFetchApp.fetch( 411 | "http://s.nfangbian.com/shortlink/create", 412 | option 413 | ); 414 | if (JSON.parse(responseLinkShort.getContentText()).code == 0) { 415 | returnText = 416 | "以下数据来自短链,由XiaoMao加工:" + 417 | "\n" + 418 | "\n" + 419 | "生成的短链接:" + 420 | JSON.parse(responseLinkShort.getContentText()).data.short_url; 421 | } else { 422 | returnText = 423 | "" + JSON.parse(responseLinkShort.getContentText()).msg + ""; 424 | } 425 | } catch (e) { 426 | return returnText; 427 | } 428 | return returnText; 429 | }; 430 | /** 431 | * 天气api查询✅ 432 | * @param location 433 | * @returns 434 | */ 435 | const getWeatherApi = (location) => { 436 | let responseWeather = null; 437 | let returnText = ""; 438 | 439 | if (location == "") { 440 | returnText = "查询的内容为空,请在指令后面加上地区再试吧~"; 441 | return returnText; 442 | } 443 | 444 | try { 445 | responseWeather = UrlFetchApp.fetch( 446 | "https://query.asilu.com/weather/baidu/?city=" + 447 | location + 448 | "×=" + 449 | new Date().getTime(), 450 | { 451 | muteHttpExceptions: true, 452 | } 453 | ); 454 | let jsonData = JSON.parse(responseWeather.getContentText()); 455 | if (jsonData.weather.length) { 456 | returnText = 457 | "以下数据来自爱思路,由XiaoMao加工:" + 458 | jsonData.city + 459 | "天气(数据更新时间:" + 460 | jsonData.date + 461 | jsonData.update_time + 462 | ")" + 463 | "\n"; 464 | 465 | jsonData.weather.forEach((el) => { 466 | returnText = 467 | returnText + 468 | "\n" + 469 | "\n" + 470 | el.date + 471 | "\n" + 472 | "☁️天气状况:" + 473 | el.weather + 474 | "\n" + 475 | "☁️温度:" + 476 | el.temp + 477 | "\n" + 478 | "☁️风向:" + 479 | el.wind; 480 | }); 481 | } else { 482 | returnText = "Oh! 出错了!"; 483 | } 484 | } catch (e) { 485 | return returnText; 486 | } 487 | return returnText; 488 | }; 489 | 490 | /** 491 | * 热榜查询 492 | * @param type 493 | * @returns 494 | */ 495 | const getHotList = (type) => { 496 | let responseText = null; 497 | let returnText = 498 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 499 | 500 | let typeObj = {}; 501 | if (type == "") { 502 | returnText = 503 | "查询的热榜参数为空,请在指令后面加上参数吧~" + 504 | "\n" + 505 | "\n" + 506 | "🔥热榜查询" + 507 | "\n" + 508 | "虎扑热榜:/hot hp" + 509 | "\n" + 510 | "知乎热榜:/hot zh" + 511 | "\n" + 512 | "36氪热榜:/hot 36" + 513 | "\n" + 514 | "百度热榜:/hot bd" + 515 | "\n" + 516 | "B站热榜:/hot bz" + 517 | "\n" + 518 | "贴吧热榜:/hot tb" + 519 | "\n" + 520 | "微博热榜:/hot wb" + 521 | "\n" + 522 | "抖音热榜:/hot dy" + 523 | "\n" + 524 | "豆瓣热榜:/hot db" + 525 | "\n" + 526 | "微信热榜:/hot wx" + 527 | "\n" + 528 | "少数派热榜:/hot ss" + 529 | "\n" + 530 | "IT资讯热榜:/hot it" + 531 | "\n" + 532 | "IT资讯新榜:/hot itn" + 533 | "\n" + 534 | "\n" + 535 | "🌟趣榜查询" + 536 | "\n" + 537 | "历史上的今天:/hot ls" + 538 | "\n" + 539 | "微信美食榜:/hot ms" + 540 | "\n" + 541 | "微信财经榜:/hot cj" + 542 | "\n" + 543 | "微信搞笑榜:/hot gx" + 544 | "\n" + 545 | "微信科技榜:/hot kj" + 546 | "\n" + 547 | "微信八卦榜:/hot bg" + 548 | "\n" + 549 | "微信星座榜:/hot xz" + 550 | "\n" + 551 | "微信旅游榜:/hot ly"; 552 | return returnText; 553 | } else { 554 | let typeList = [ 555 | { 556 | name: "虎扑热榜", 557 | type: "hp", 558 | params: "huPu", 559 | }, 560 | { 561 | name: "知乎热榜", 562 | type: "zh", 563 | params: "zhihuHot", 564 | }, 565 | { 566 | name: "36氪热榜", 567 | type: "36", 568 | params: "36Ke", 569 | }, 570 | { 571 | name: "百度热榜", 572 | type: "bd", 573 | params: "baiduRD", 574 | }, 575 | { 576 | name: "B站热榜", 577 | type: "bz", 578 | params: "bili", 579 | }, 580 | { 581 | name: "贴吧热榜", 582 | type: "tb", 583 | params: "baiduRY", 584 | }, 585 | { 586 | name: "微博热榜", 587 | type: "wb", 588 | params: "wbHot", 589 | }, 590 | { 591 | name: "抖音热榜", 592 | type: "dy", 593 | params: "douyinHot", 594 | }, 595 | { 596 | name: "豆瓣热榜", 597 | type: "db", 598 | params: "douban", 599 | }, 600 | { 601 | name: "微信热榜", 602 | type: "wx", 603 | params: "wxHot", 604 | }, 605 | { 606 | name: "少数派热榜", 607 | type: "ss", 608 | params: "ssPai", 609 | }, 610 | { 611 | name: "IT资讯热榜", 612 | type: "it", 613 | params: "itInfo", 614 | }, 615 | { 616 | name: "IT资讯新榜", 617 | type: "itn", 618 | params: "itNews", 619 | }, 620 | { 621 | name: "历史上的今天", 622 | type: "ls", 623 | params: "history", 624 | }, 625 | { 626 | name: "微信美食榜", 627 | type: "ms", 628 | params: "wxFood", 629 | }, 630 | { 631 | name: "微信搞笑榜", 632 | type: "gx", 633 | params: "wxJoke", 634 | }, 635 | { 636 | name: "微信财经榜", 637 | type: "cj", 638 | params: "wxMoney", 639 | }, 640 | { 641 | name: "微信科技榜", 642 | type: "kj", 643 | params: "wxKeJi", 644 | }, 645 | { 646 | name: "微信八卦榜", 647 | type: "bg", 648 | params: "wxBaGua", 649 | }, 650 | { 651 | name: "微信星座榜", 652 | type: "xz", 653 | params: "wxXingZuo", 654 | }, 655 | { 656 | name: "微信旅游榜", 657 | type: "ly", 658 | params: "wxLvYou", 659 | }, 660 | ]; 661 | typeObj = typeList.find((el) => el.type == type); 662 | 663 | if (typeObj == undefined) { 664 | returnText = "查询参数匹配失败,请核对参数正确性!"; 665 | return returnText; 666 | } 667 | } 668 | 669 | try { 670 | responseText = UrlFetchApp.fetch( 671 | "https://api.vvhan.com/api/hotlist/" + typeObj.params, 672 | { 673 | muteHttpExceptions: true, 674 | } 675 | ); 676 | let jsonData = JSON.parse(responseText.getContentText()); 677 | 678 | let dealText = "内容获取失败,请稍后再试~"; 679 | if (jsonData.success && jsonData.data.length) { 680 | dealText = ""; 681 | jsonData.data.forEach((el, i) => { 682 | dealText = 683 | dealText + 684 | "[" + 685 | el.index + 686 | "] " + 687 | (el.hasOwnProperty("hot") 688 | ? "[" + 689 | (i < 5 ? "🔥" : "") + 690 | "热度:" + 691 | el.hot 692 | .toString() 693 | .replace("热度", "") 694 | .replace("万", "w") 695 | .replace("千", "k") + 696 | "] " 697 | : "") + 698 | "" + 701 | el.title + 702 | "" + 703 | "\n"; 704 | }); 705 | } 706 | returnText = 707 | "以下数据来自韩小韩,由XiaoMao加工:" + 708 | "\n" + 709 | "\n" + 710 | "🌟" + 711 | "以下内容来自" + 712 | typeObj.name + 713 | "" + 714 | "\n" + 715 | "数据更新时间:" + 716 | (jsonData.update_time || "-") + 717 | "" + 718 | "\n" + 719 | "\n" + 720 | dealText; 721 | } catch (e) { 722 | return returnText; 723 | } 724 | return returnText; 725 | }; 726 | 727 | /** 728 | * 星座运势 729 | * @param type 730 | * @returns 731 | */ 732 | const getHoroscopeList = (type) => { 733 | let responseText = null; 734 | let returnText = 735 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 736 | 737 | let typeObj = {}; 738 | let timeObj = {}; 739 | let typeArr = []; 740 | let timeObjName = ""; 741 | if (type == "") { 742 | returnText = 743 | "查询的星座参数为空,请在指令后面加上参数吧~" + 744 | "\n" + 745 | "\n" + 746 | "🌌支持的星座" + 747 | "\n" + 748 | "♈️白羊座:baiyang" + 749 | "\n" + 750 | "♉️金牛座:jinniu" + 751 | "\n" + 752 | "♊️双子座:shuangzi" + 753 | "\n" + 754 | "♌️狮子座:shizi" + 755 | "\n" + 756 | "♍️处女座:chunv" + 757 | "\n" + 758 | "♎️天秤座:tiancheng" + 759 | "\n" + 760 | "♏️天蝎座:tianxie" + 761 | "\n" + 762 | "♐️射手座:sheshou" + 763 | "\n" + 764 | "♑️摩羯座:mojie" + 765 | "\n" + 766 | "♒️水瓶座:shuiping" + 767 | "\n" + 768 | "♓️双鱼座:shuangyu" + 769 | "\n" + 770 | "\n" + 771 | "🌟支持的范围" + 772 | "\n" + 773 | "今日运势:D" + 774 | "\n" + 775 | "明日运势:T" + 776 | "\n" + 777 | "本周运势:W" + 778 | "\n" + 779 | "本月运势:M" + 780 | "\n" + 781 | "本年运势:Y" + 782 | "\n" + 783 | "\n" + 784 | "🔥星座运势查询" + 785 | "\n" + 786 | "双子座今日运势:/xz shuangzi+D" + 787 | "\n" + 788 | "天秤座本月运势:/xz tiancheng+M"; 789 | return returnText; 790 | } else { 791 | let typeList = [ 792 | { name: "♈️白羊座", type: "baiyang", params: "aries" }, 793 | { name: "♉️金牛座", type: "jinniu", params: "taurus" }, 794 | { name: "♊️双子座", type: "shuangzi", params: "gemini" }, 795 | { name: "♋️巨蟹座", type: "juxie", params: "cancer" }, 796 | { name: "♌️狮子座", type: "shizi", params: "leo" }, 797 | { name: "♍️处女座", type: "chunv", params: "virgo" }, 798 | { name: "♎️天秤座", type: "tiancheng", params: "libra" }, 799 | { name: "♏️天蝎座", type: "tianxie", params: "scorpio" }, 800 | { name: "♐️射手座", type: "sheshou", params: "sagittarius" }, 801 | { name: "♑️摩羯座", type: "mojie", params: "capricorn" }, 802 | { name: "♒️水瓶座", type: "shuiping", params: "aquarius" }, 803 | { name: "♓️双鱼座", type: "shuangyu", params: "pisces" }, 804 | ]; 805 | let timeList = [ 806 | { 807 | name: "今日运势", 808 | type: "D", 809 | params: "today", 810 | }, 811 | { 812 | name: "明日运势", 813 | type: "T", 814 | params: "nextday", 815 | }, 816 | { 817 | name: "本周运势", 818 | type: "W", 819 | params: "week", 820 | }, 821 | { 822 | name: "本月运势", 823 | type: "M", 824 | params: "month", 825 | }, 826 | { 827 | name: "本年运势", 828 | type: "Y", 829 | params: "year", 830 | }, 831 | ]; 832 | if (type) { 833 | typeArr = type.split("+"); 834 | } 835 | 836 | typeObj = typeList.find((el) => el.type == typeArr[0]); 837 | timeObj = timeList.find((el) => el.type == (typeArr[1] || "D")); 838 | if (typeObj == undefined || timeObj == undefined) { 839 | returnText = "查询参数匹配失败,请核对参数正确性!"; 840 | return returnText; 841 | } 842 | timeObjName = timeObj.name.slice(0, 2); 843 | } 844 | 845 | try { 846 | responseText = UrlFetchApp.fetch( 847 | "https://api.vvhan.com/api/horoscope?type=" + 848 | typeObj.params + 849 | "&time=" + 850 | timeObj.params, 851 | { 852 | muteHttpExceptions: true, 853 | } 854 | ); 855 | let jsonData = JSON.parse(responseText.getContentText()); 856 | 857 | let dealText = "内容获取失败,请稍后再试~"; 858 | if (jsonData.success) { 859 | dealText = ""; 860 | if (jsonData.data.hasOwnProperty("todo")) { 861 | dealText = 862 | dealText + 863 | timeObjName + 864 | "吉凶宜忌:" + 865 | "\n" + 866 | "✅适宜动作:" + 867 | (jsonData.data.todo.yi || "- ") + 868 | "\n" + 869 | "❎忌讳动作:" + 870 | (jsonData.data.todo.ji || "- ") + 871 | "\n" + 872 | "🔢幸运数字:" + 873 | (jsonData.data.luckynumber || "- ") + 874 | "\n" + 875 | "🎨幸运颜色:" + 876 | (jsonData.data.luckycolor || "- ") + 877 | "\n" + 878 | "❤️速配星座:" + 879 | (jsonData.data.luckyconstellation || "- ") + 880 | "\n" + 881 | "💔提防星座:" + 882 | (jsonData.data.badconstellation || "- ") + 883 | "\n" + 884 | "💮运势短评:" + 885 | (jsonData.data.shortcomment || "- ") + 886 | "\n\n"; 887 | } 888 | if (jsonData.data.hasOwnProperty("fortune")) { 889 | let starIndex = "🌟🌟🌟🌟🌟"; 890 | dealText = 891 | dealText + 892 | timeObjName + 893 | "运势:" + 894 | "\n" + 895 | "🈴综合运势:" + 896 | starIndex.slice(0, 2 * parseInt(jsonData.data.fortune.all)) + 897 | "\n" + 898 | "💞爱情运势:" + 899 | starIndex.slice(0, 2 * parseInt(jsonData.data.fortune.love)) + 900 | "\n" + 901 | "📖事业运势:" + 902 | starIndex.slice(0, 2 * parseInt(jsonData.data.fortune.work)) + 903 | "\n" + 904 | "💰财富运势:" + 905 | starIndex.slice(0, 2 * parseInt(jsonData.data.fortune.money)) + 906 | "\n" + 907 | "💪健康运势:" + 908 | starIndex.slice(0, 2 * parseInt(jsonData.data.fortune.health)) + 909 | "\n\n"; 910 | } 911 | if (jsonData.data.hasOwnProperty("index")) { 912 | dealText = 913 | dealText + 914 | timeObjName + 915 | "指数:" + 916 | "\n" + 917 | "🈴综合运势:" + 918 | jsonData.data.index.all + 919 | "\n" + 920 | "💞爱情运势:" + 921 | jsonData.data.index.love + 922 | "\n" + 923 | "📖事业运势:" + 924 | jsonData.data.index.work + 925 | "\n" + 926 | "💰财富运势:" + 927 | jsonData.data.index.money + 928 | "\n" + 929 | "💪健康运势:" + 930 | jsonData.data.index.health + 931 | "\n\n"; 932 | } 933 | if (jsonData.data.hasOwnProperty("fortunetext")) { 934 | dealText = 935 | dealText + 936 | timeObjName + 937 | "运势解析:" + 938 | "\n" + 939 | "🈴综合运势:" + 940 | jsonData.data.fortunetext.all + 941 | "\n" + 942 | "💞爱情运势:" + 943 | jsonData.data.fortunetext.love + 944 | "\n" + 945 | "📖事业运势:" + 946 | jsonData.data.fortunetext.work + 947 | "\n" + 948 | "💰财富运势:" + 949 | jsonData.data.fortunetext.money + 950 | "\n" + 951 | "💪健康运势:" + 952 | jsonData.data.fortunetext.health + 953 | "\n" + 954 | "😮‍💨解压秘诀:" + 955 | (jsonData.data.fortunetext.decompression || "- ") + 956 | "\n" + 957 | "😄开运秘诀:" + 958 | (jsonData.data.fortunetext.openluck || "- ") + 959 | "\n\n"; 960 | } 961 | } 962 | returnText = 963 | "以下数据来自韩小韩,由XiaoMao加工:" + 964 | "\n" + 965 | "\n" + 966 | typeObj.name + 967 | "- " + 968 | jsonData.data.type + 969 | "(" + 970 | jsonData.data.time + 971 | ")" + 972 | "\n" + 973 | "\n" + 974 | dealText; 975 | } catch (e) { 976 | return returnText; 977 | } 978 | return returnText; 979 | }; 980 | 981 | /** 982 | * 豆瓣电影排行 983 | * @param params 984 | * @returns 985 | */ 986 | const getDouBan = (params) => { 987 | let responseText = null; 988 | let returnText = 989 | "查询结果受运营商网络管制,本次通信被异常终止,此管控行为非人为可控,请稍后再试~"; 990 | try { 991 | responseText = UrlFetchApp.fetch("https://api.vvhan.com/api/douban", { 992 | muteHttpExceptions: true, 993 | }); 994 | let jsonData = JSON.parse(responseText.getContentText()); 995 | 996 | let dealText = "内容获取失败,请稍后再试~"; 997 | if (jsonData.success && jsonData.data.length) { 998 | dealText = ""; 999 | jsonData.data.forEach((el, i) => { 1000 | dealText = 1001 | dealText + 1002 | "[" + 1003 | (i + 1) + 1004 | "] " + 1005 | "" + 1008 | el.title + 1009 | "" + 1010 | "\n" + 1011 | "豆瓣评分:" + 1012 | el.info.pingfen + 1013 | "\n" + 1014 | "演员名单:" + 1015 | el.info.yanyuan + 1016 | "\n" + 1017 | "评价人数:" + 1018 | el.info.pingjia + 1019 | "\n" + 1020 | "\n"; 1021 | }); 1022 | } 1023 | returnText = 1024 | "以下数据来自韩小韩,由XiaoMao加工:" + 1025 | "\n" + 1026 | "\n" + 1027 | "🎬豆瓣电影排行" + 1028 | "\n" + 1029 | "数据更新时间:" + 1030 | (jsonData.time || "-") + 1031 | "" + 1032 | "\n" + 1033 | "\n" + 1034 | dealText; 1035 | } catch (e) { 1036 | return returnText; 1037 | } 1038 | return returnText; 1039 | }; 1040 | 1041 | /** 1042 | * 话痨排行榜 1043 | * @returns 1044 | */ 1045 | const getChatterboxUser = (useId, userJson) => { 1046 | if (useId.toString().indexOf("-") == -1) { 1047 | return "请在非私密群组内使用话痨排行榜功能!"; 1048 | } 1049 | let textListJson = readSpreadsheet(EXECNAME); 1050 | let textList = textListJson.slice(3, textListJson.length).reverse(); 1051 | let ChatterboxUserListJson = {}; 1052 | for (let item in textList) { 1053 | let isToday = isSameDay(textList[item][0]); 1054 | if ( 1055 | textList[item][1] != "" && 1056 | textList[item][6].toString() == useId.toString() && 1057 | isToday 1058 | ) { 1059 | if (ChatterboxUserListJson.hasOwnProperty(textList[item][1].toString())) { 1060 | ChatterboxUserListJson[textList[item][1].toString()].total += 1; 1061 | } else { 1062 | ChatterboxUserListJson[textList[item][1].toString()] = { 1063 | total: 1, 1064 | userNameKey: textList[item][2], 1065 | userName: textList[item][3], 1066 | userId: textList[item][1], 1067 | }; 1068 | } 1069 | } else if (!isToday) { 1070 | break; 1071 | } 1072 | } 1073 | let ChatterboxUserList = sortByTotalDescending(ChatterboxUserListJson); 1074 | let textHtml = 1075 | "💬「" + userJson.chat.title + "」今日话痨排行榜" + "\n\n"; 1076 | let textIndex = 1; 1077 | let textRank = ["🥇", "🥈", "🥉"]; 1078 | if (ChatterboxUserList.length) { 1079 | ChatterboxUserList.map((el) => { 1080 | if (el.userName !== "Telegram" && el.userName !== "XiaoMaoBot") { 1081 | textHtml = 1082 | textHtml + 1083 | (textIndex < 4 1084 | ? textRank[textIndex - 1] 1085 | : textIndex < 10 1086 | ? "0" + textIndex 1087 | : textIndex) + 1088 | " " + 1089 | el.userName + 1090 | "\n" + 1091 | "发言次数:" + 1092 | el.total + 1093 | "\n\n"; 1094 | textIndex += 1; 1095 | } 1096 | }); 1097 | } else { 1098 | textHtml = textHtml + "当前群组暂无发言数据!"; 1099 | } 1100 | 1101 | return textHtml; 1102 | }; 1103 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/Core.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 核心能力类 3 | * 4 | * 无需改动 5 | */ 6 | 7 | /** 8 | * tg api服务 9 | * @param data 10 | */ 11 | const linkBot = (data) => { 12 | try { 13 | let res = UrlFetchApp.fetch("https://api.telegram.org/bot" + BOTID + "/", data); 14 | return res.getContentText() 15 | } catch (error) { 16 | } 17 | }; 18 | 19 | /** 20 | * 读取表格数据 21 | * @returns [[],[],...] 22 | */ 23 | const readSpreadsheet = (key) => { 24 | // 获取当前活跃的表格 25 | let spreadsheet = SpreadsheetApp.getActiveSpreadsheet(); 26 | let sheet = spreadsheet.getSheetByName(key); 27 | // 获取工作表的数据范围 28 | let range = sheet.getDataRange(); 29 | // 获取表格内容作为二维数组 30 | return range.getValues() ?? []; 31 | }; 32 | 33 | /** 34 | * 组装关键字回复内容 html 35 | * @param input 36 | * a标签不可再次嵌套标签 37 | * @returns html/any 38 | */ 39 | const convertString = (input) => { 40 | // 检查输入是否是字符串类型 41 | if (typeof input !== "string") { 42 | return input.toString(); 43 | } 44 | // 检查是否有嵌套的 标签 45 | const nestedUrlMatch = input.match(/(.*?)<\/a>/); 46 | if (nestedUrlMatch) { 47 | const url = nestedUrlMatch[1]; 48 | // 提取 标签之前的内容和之后的内容 49 | const [before, after] = input.split(/.*?<\/a>/); 50 | // 包裹整个输入内容到新的 标签中,并插入 href 51 | const result = `${before}${after}`; 52 | return result; 53 | } 54 | 55 | // 如果不匹配 标签,则直接返回输入 56 | return input; 57 | }; 58 | 59 | /** 60 | * 构建关键字列表 61 | * @param readSpreadsheet() 62 | * @returns replyList:[{keyword: [],replyWord: '',replyWordMore: [])},...] 63 | */ 64 | const getKeyWords = () => { 65 | let keys = readSpreadsheet(KEYPARAMS); 66 | // 关键字属性集合 67 | let keyName = []; 68 | // 函数返回 69 | let replyList = []; 70 | if (keys.length > 3) { 71 | keys.slice(3, keys.length).map((item) => { 72 | // 获取到对象key 73 | keyName = item[0].split(","); 74 | // 获取内容项 value list 75 | let itemWords = item.splice(1, item.length); 76 | if (itemWords.length) { 77 | // 当前单元格内容 78 | let keyValueList = []; 79 | // 获取解析模式 80 | if (itemWords[0] == "HTML") { 81 | itemWords.slice(1).map((word) => { 82 | // 切割换行 83 | let wordsList = word.toString().split("\n"); 84 | let keyValue = ""; 85 | if (wordsList.length) { 86 | // 处理为通用html text 87 | wordsList.map((list, listIndex) => { 88 | keyValue = keyValue + convertString(list) + "\n"; 89 | }); 90 | keyValueList.push(keyValue); 91 | } 92 | }); 93 | replyList.push({ 94 | keyword: keyName, 95 | parseMode: "HTML", 96 | replyWord: keyValueList[0], 97 | replyWordMore: keyValueList 98 | .slice(1, keyValueList.length) 99 | .filter((item) => item !== "\n"), 100 | }); 101 | } else if (itemWords[0] == "MarkdownV2") { 102 | keyValueList = itemWords.slice(1); 103 | replyList.push({ 104 | keyword: keyName, 105 | parseMode: "MarkdownV2", 106 | replyWord: keyValueList[0], 107 | replyWordMore: keyValueList 108 | .slice(1, keyValueList.length) 109 | .filter((item) => item !== "\n"), 110 | }); 111 | } else if ( 112 | ["GraphicMessage", "VideoMessage"].indexOf(itemWords[0]) != -1 113 | ) { 114 | let mediaArr = itemWords[1].split("\n").filter(Boolean); 115 | let messageText = itemWords[2] ?? ""; 116 | let mediaList = []; 117 | if (mediaArr.length) { 118 | mediaArr.map((el, i) => { 119 | let mediaObj = { 120 | type: itemWords[0] == "GraphicMessage" ? "photo" : "video", 121 | media: el, 122 | }; 123 | if (i == mediaArr.length - 1) { 124 | mediaObj.caption = messageText; 125 | mediaObj.parse_mode = "MarkdownV2"; 126 | mediaObj.disable_web_page_preview = true; 127 | } 128 | mediaList.push(mediaObj); 129 | }); 130 | } 131 | replyList.push({ 132 | keyword: keyName, 133 | parseMode: "GraphicMessage", 134 | replyWord: JSON.stringify(mediaList), 135 | replyWordMore: [], 136 | }); 137 | } 138 | } else { 139 | replyList.push({ 140 | keyword: keyName, 141 | parseMode: "HTML", 142 | replyWord: "关键字内容为空,或获取失败,请检查关键字表单内容格式!", 143 | replyWordMore: [], 144 | }); 145 | } 146 | }); 147 | } 148 | return replyList; 149 | }; 150 | 151 | /** 152 | * 使用脚本级别缓存服务 153 | * 获取关键字 154 | */ 155 | let Cache = CacheService.getScriptCache(); 156 | const getCacheData = () => { 157 | if (cacheExpirationStatus) { 158 | Cache.remove("keyParamsList"); 159 | } 160 | let replayList = Cache.get("keyParamsList"); 161 | if (!replayList) { 162 | replayList = getKeyWords(); 163 | Cache.put( 164 | "keyParamsList", 165 | JSON.stringify(replayList), 166 | cacheExpirationInSeconds 167 | ); 168 | autoReply = replayList; 169 | } else { 170 | autoReply = JSON.parse(replayList); 171 | } 172 | }; 173 | 174 | /** 175 | * 获取屏蔽群组及管理员列表 176 | */ 177 | const getCacheAuthorityList = () => { 178 | if (cacheExpirationStatus) { 179 | Cache.remove("authorityManagementGroup"); 180 | Cache.remove("authorityManagement"); 181 | } 182 | let authorityGroupList = Cache.get("authorityManagementGroup"); 183 | let authorityList = Cache.get("authorityManagement"); 184 | let manageList = []; 185 | if (!authorityGroupList) { 186 | manageList = readSpreadsheet(AUTHORITYMANAGEMENT).slice(2); 187 | authorityGroupList = manageList[0].slice(1).filter(Boolean); 188 | Cache.put( 189 | "authorityManagementGroup", 190 | JSON.stringify(authorityGroupList), 191 | cacheExpirationInSeconds 192 | ); 193 | forGotList = authorityGroupList; 194 | } else { 195 | forGotList = JSON.parse(authorityGroupList); 196 | } 197 | 198 | if (!authorityList) { 199 | manageList.length ? "" : readSpreadsheet(AUTHORITYMANAGEMENT).slice(2); 200 | authorityList = manageList[1].slice(1).filter(Boolean).concat([KingId]); 201 | Cache.put( 202 | "authorityManagement", 203 | JSON.stringify(authorityList), 204 | cacheExpirationInSeconds 205 | ); 206 | PermissionReleaseList = authorityList; 207 | } else { 208 | PermissionReleaseList = JSON.parse(authorityList); 209 | } 210 | }; 211 | 212 | /** 213 | * 将讯息进行Google表格内存储 214 | * @param {*} MESSAGE 215 | */ 216 | const setStorage = (MESSAGE, TYPE) => { 217 | let time = getNowDate(); 218 | let userID, 219 | userName, 220 | userAllName, 221 | messageSource, 222 | messageSourceID, 223 | messageType, 224 | messageContent = ""; 225 | 226 | switch (TYPE) { 227 | case "POSTDATA": 228 | messageType = "主动发起"; 229 | break; 230 | case "CALLBACK": 231 | messageType = "键盘回调"; 232 | break; 233 | case "CHANNELPOST": 234 | messageType = "频道监听"; 235 | break; 236 | default: 237 | messageType = "--自动回复"; 238 | break; 239 | } 240 | 241 | let spreadSheet = SpreadsheetApp.openById(EXECID); 242 | let Sheet = spreadSheet.getSheetByName(EXECNAME); 243 | let lastSheetRow = spreadSheet.getLastRow(); 244 | 245 | if (TYPE != "MESSAGEBACK" && TYPE != "CHANNELPOST") { 246 | userID = MESSAGE.message.from.id.toString(); 247 | 248 | userName = 249 | MESSAGE.message.from.username != undefined 250 | ? "@" + MESSAGE.message.from.username 251 | : "🈚️用户名"; 252 | 253 | userAllName = 254 | (MESSAGE.message.from.first_name != undefined 255 | ? MESSAGE.message.from.first_name 256 | : "") + 257 | (MESSAGE.message.from.last_name != undefined 258 | ? MESSAGE.message.from.last_name 259 | : ""); 260 | 261 | if (userAllName == "") { 262 | userAllName = "该用户未设置昵称"; 263 | } 264 | let messageInfoType = getMessageType(MESSAGE, "message"); 265 | messageContent = 266 | messageInfoType + 267 | (messageInfoType.indexOf("[文本消息]") != -1 268 | ? MESSAGE.message.text 269 | : MESSAGE.message?.caption); 270 | 271 | messageSource = 272 | (MESSAGE.message.chat.type == "supergroup" 273 | ? MESSAGE.message.chat.title 274 | : "") + 275 | "(" + 276 | (MESSAGE.message.chat.type == "supergroup" 277 | ? "群聊消息" 278 | : MESSAGE.message.chat.type == "private" 279 | ? "私聊消息" 280 | : "未知渠道") + 281 | ")"; 282 | 283 | messageSourceID = MESSAGE.message.chat.id.toString(); 284 | 285 | //用户ID 286 | Sheet.getRange(lastSheetRow + 1, 2).setValue(userID); 287 | //用户名称 288 | Sheet.getRange(lastSheetRow + 1, 3).setValue(userName); 289 | // 用户昵称 290 | Sheet.getRange(lastSheetRow + 1, 4).setValue(userAllName); 291 | // 消息来源 292 | Sheet.getRange(lastSheetRow + 1, 6).setValue(messageSource); 293 | // 消息来源ID 294 | Sheet.getRange(lastSheetRow + 1, 7).setValue(messageSourceID); 295 | // 消息内容 296 | Sheet.getRange(lastSheetRow + 1, 8).setValue(messageContent); 297 | } else if (TYPE == "CHANNELPOST") { 298 | //用户ID 299 | Sheet.getRange(lastSheetRow + 1, 2).setValue(MESSAGE.channel_post.chat.id); 300 | //用户名称 301 | Sheet.getRange(lastSheetRow + 1, 3).setValue("[频道]"); 302 | // 用户昵称 303 | Sheet.getRange(lastSheetRow + 1, 4).setValue( 304 | MESSAGE.channel_post.chat.title 305 | ); 306 | // 消息来源 307 | Sheet.getRange(lastSheetRow + 1, 6).setValue("(频道消息)"); 308 | // 消息来源ID 309 | Sheet.getRange(lastSheetRow + 1, 7).setValue(MESSAGE.channel_post.chat.id); 310 | let messageInfoType = getMessageType(MESSAGE, "channel_post"); 311 | messageContent = 312 | messageInfoType + 313 | (messageInfoType.indexOf("[文本消息]") != -1 314 | ? MESSAGE.channel_post.text 315 | : MESSAGE.channel_post?.caption); 316 | // 消息内容 317 | Sheet.getRange(lastSheetRow + 1, 8).setValue(messageContent); 318 | } 319 | 320 | //发起时间 321 | Sheet.getRange(lastSheetRow + 1, 1).setValue(time); 322 | // 消息类型 323 | Sheet.getRange(lastSheetRow + 1, 5).setValue(messageType); 324 | // 消息JSON 325 | Sheet.getRange(lastSheetRow + 1, 9).setValue(JSON.stringify(MESSAGE)); 326 | }; 327 | 328 | 329 | /** 330 | * 获取api返回数据列表 331 | * @param data 332 | */ 333 | const getApiBackList = (data) => { 334 | let list = []; 335 | for (let i = 0; i < data.length; i++) { 336 | const e = data[i]; 337 | if (e.payload.hasOwnProperty("reply_to_message_id")) { 338 | try { 339 | let linkData = linkBot(e); 340 | setStorage(e, "MESSAGEBACK"); 341 | linkData = JSON.parse(linkData); 342 | if ( 343 | linkData?.result?.reply_to_message?.chat && 344 | linkData.result.message_id && 345 | linkData.result.chat.type 346 | ) { 347 | const chatId = linkData.result.reply_to_message.chat.id.toString(); 348 | const messageId = linkData.result.message_id.toString(); 349 | const chatType = linkData.result.chat.type; 350 | list.push([chatId, messageId, chatType]); 351 | } else { 352 | } 353 | } catch (error) {} 354 | } 355 | } 356 | return list 357 | } -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/Manage.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 群管类 3 | * 4 | * 无需改动 5 | */ 6 | 7 | /** 8 | * 删除信息 - 不建议主动调用,建议使用deleteMessageApi 9 | * @param params 10 | * @param type 1主动删除 2调用删除 3删除回复 11 | */ 12 | const deleteUserMessage = (params, type = 1) => { 13 | let payloadDeletePostData = { 14 | method: "deleteMessage", 15 | chat_id: "", 16 | message_id: "", 17 | }; 18 | if (type == 1) { 19 | // 删除信息 20 | let userJsonText = params.reply_to_message.text; 21 | let startIndex = userJsonText.indexOf('message_id":'); 22 | let endIndex = userJsonText.indexOf(',"from'); 23 | let message_id = userJsonText.substring(startIndex + 12, endIndex); 24 | let firstIndex = userJsonText.indexOf('chat":{"id":'); 25 | let lastIndex = userJsonText.indexOf(',"title'); 26 | let chat_id = userJsonText.substring(firstIndex + 12, lastIndex); 27 | payloadDeletePostData.chat_id = chat_id; 28 | payloadDeletePostData.message_id = message_id; 29 | } else if (type == 3) { 30 | payloadDeletePostData.chat_id = params.reply_to_message.chat.id.toString(); 31 | payloadDeletePostData.message_id = params.message_id.toString(); 32 | } else { 33 | payloadDeletePostData.chat_id = params.reply_to_message.chat.id.toString(); 34 | payloadDeletePostData.message_id = 35 | params.reply_to_message.message_id.toString(); 36 | } 37 | 38 | try { 39 | linkBot({ 40 | method: "post", 41 | payload: payloadDeletePostData, 42 | }); 43 | } catch (e) {} 44 | }; 45 | 46 | /** 47 | * api 自动删除消息 48 | * @param key1 chat_id 49 | * @param key2 message_id 50 | */ 51 | const deleteMessageApi = (key1, key2) => { 52 | let payloadDeletePostData = { 53 | method: "deleteMessage", 54 | chat_id: key1, 55 | message_id: key2, 56 | }; 57 | try { 58 | linkBot({ 59 | method: "post", 60 | payload: payloadDeletePostData, 61 | }); 62 | } catch (e) {} 63 | }; 64 | 65 | /** 66 | * 获取管理员列表 67 | * @returns 68 | */ 69 | const getPermissionList = (userJson) => { 70 | let PermissionGroupId = ""; 71 | try { 72 | if (userJson.chat.type == "supergroup") { 73 | PermissionGroupId = userJson.reply_to_message.chat.id.toString(); 74 | let resultAdministrators = UrlFetchApp.fetch( 75 | "https://api.telegram.org/bot" + 76 | BOTID + 77 | "/" + 78 | "getChatAdministrators?chat_id=" + 79 | PermissionGroupId 80 | ); 81 | let userAdministrators = JSON.parse( 82 | resultAdministrators.getContentText() 83 | ).result.map((el) => el.user.id.toString()); 84 | PermissionReleaseList = PermissionReleaseList.concat(userAdministrators); 85 | } 86 | } catch (error) {} 87 | 88 | return PermissionReleaseList; 89 | }; 90 | 91 | /** 92 | * 解除封禁用户 93 | * @param userJson 94 | * @returns 95 | */ 96 | const getUnBanUser = (userJson) => { 97 | if (!userJson.hasOwnProperty("reply_to_message")) { 98 | returnText = "操作失败!未找到指定用户,请引用对方消息再进行操作。"; 99 | return returnText; 100 | } 101 | if (getPermissionList(userJson).indexOf(userJson.from.id.toString()) == -1) { 102 | returnText = 103 | "Bot用户封禁功能仅开放于Bot管理者,请拉取最新版XiaoMaoBot代码部署后再试吧!"; 104 | return returnText; 105 | } else if (PermissionRelease && userJson.chat.type == "supergroup") { 106 | let payloadPostData = { 107 | method: "unbanChatMember", 108 | only_if_banned: true, 109 | chat_id: userJson.reply_to_message.chat.id.toString(), 110 | user_id: userJson.reply_to_message.from.id.toString(), 111 | }; 112 | try { 113 | linkBot({ 114 | method: "post", 115 | payload: payloadPostData, 116 | }); 117 | } catch (e) {} 118 | 119 | let payloadPostData2 = { 120 | method: "sendMessage", 121 | chat_id: userJson.reply_to_message.chat.id.toString(), 122 | text: 123 | "📣来自XiaoMaoBot管理员的操作提醒" + 124 | "\n" + 125 | "\n" + 126 | "\n" + 127 | "===========================" + 128 | "\n" + 129 | "\n" + 130 | "" + 131 | payloadPostData.user_id + 132 | "您已被XiaoMao管理员解除封禁,注意不要再次违规哟," + 133 | " XiaoMao群聊 点击加入 " + 134 | "" + 135 | "\n" + 136 | "\n" + 137 | "===========================" + 138 | "\n", 139 | parse_mode: "HTML", 140 | reply_markup: JSON.stringify(keyboardFollowManageParams), 141 | disable_web_page_preview: true, 142 | }; 143 | linkBot({ 144 | method: "post", 145 | payload: payloadPostData2, 146 | }); 147 | 148 | return "操作成功!"; 149 | } else if (userJson.chat.type == "private") { 150 | if (!userJson.hasOwnProperty("reply_to_message")) { 151 | returnText = 152 | "未找到引用消息内容,Bot用户封禁功能需要开启私人消息推送服务,请于 XiaoMao_TgBot仓库 👈 中查看开启及使用方式。"; 153 | return returnText; 154 | } else { 155 | if ( 156 | userJson.reply_to_message.from.username != "Xiao_MaoMao_bot" && 157 | userJson.reply_to_message.from.is_bot != true && 158 | userJson.chat.type == "private" 159 | ) { 160 | returnText = "Bot用户封禁功能仅限于回复Bot端私聊消息喔!"; 161 | return returnText; 162 | } else { 163 | try { 164 | let payloadPostData = { 165 | method: "unbanChatMember", 166 | only_if_banned: true, 167 | chat_id: "", 168 | user_id: "", 169 | }; 170 | if (userJson.reply_to_message.text.indexOf("来自[群聊]")) { 171 | let textReply = userJson.reply_to_message.text; 172 | let sub_1 = textReply.indexOf("chat"); 173 | let sub_Text = textReply.substring(sub_1 + 6, sub_1 + 30); 174 | let sub_2 = sub_Text.indexOf(":"); 175 | let sub_3 = sub_Text.indexOf(","); 176 | let sub2_Text = sub_Text.substring(sub_2 + 1, sub_3); 177 | 178 | let sub_user_1 = textReply.indexOf('"id"'); 179 | let sub_user_Text = textReply.substring( 180 | sub_user_1 + 4, 181 | sub_user_1 + 30 182 | ); 183 | let sub_user_2 = sub_user_Text.indexOf(":"); 184 | let sub_user_3 = sub_user_Text.indexOf(","); 185 | let sub2_user_Text = sub_user_Text.substring( 186 | sub_user_2 + 1, 187 | sub_user_3 188 | ); 189 | payloadPostData.user_id = sub2_user_Text.toString(); 190 | payloadPostData.chat_id = sub2_Text.toString(); 191 | 192 | try { 193 | linkBot({ 194 | method: "post", 195 | payload: payloadPostData, 196 | }); 197 | } catch (e) {} 198 | 199 | let sub__1 = textReply.indexOf("chat"); 200 | let sub__Text = textReply.substring(sub__1 + 6, sub__1 + 30); 201 | let sub__2 = sub__Text.indexOf(":"); 202 | let sub__3 = sub__Text.indexOf(","); 203 | let sub2__Text = sub__Text.substring(sub__2 + 1, sub__3); 204 | 205 | let payloadPostData2 = { 206 | method: "sendMessage", 207 | chat_id: sub2__Text.toString(), 208 | text: 209 | "📣来自XiaoMaoBot管理员的操作提醒" + 210 | "\n" + 211 | "\n" + 212 | "\n" + 213 | "===========================" + 214 | "\n" + 215 | "\n" + 216 | "" + 217 | payloadPostData.user_id + 218 | "您已被XiaoMao管理员解除封禁,注意不要再次违规哟," + 219 | " XiaoMao群聊 点击加入 " + 220 | "" + 221 | "\n" + 222 | "\n" + 223 | "===========================" + 224 | "\n", 225 | parse_mode: "HTML", 226 | reply_markup: JSON.stringify(keyboardFollowManageParams), 227 | disable_web_page_preview: true, 228 | }; 229 | linkBot({ 230 | method: "post", 231 | payload: payloadPostData2, 232 | }); 233 | } else { 234 | returnText = "出错了,封禁功能仅限来自群聊类型消息喔!"; 235 | return returnText; 236 | } 237 | 238 | return "✅ 用户 " + payloadPostData.user_id + "已解除封禁"; 239 | } catch (e) { 240 | returnText = 241 | "出错了,请将以下错误码反馈给" + 242 | " XiaoMao机器人 " + 243 | "或" + 244 | "XiaoMao群聊管理员" + 245 | "\n\n" + 246 | e; 247 | return returnText; 248 | } 249 | } 250 | } 251 | } 252 | }; 253 | 254 | /** 255 | * 封禁用户 256 | * @param userJson 257 | * @returns 258 | */ 259 | const getBanUser = (userJson) => { 260 | let timeFrame = userJson.text.replace("/ban", "") || ""; 261 | if (!userJson.hasOwnProperty("reply_to_message")) { 262 | returnText = "操作失败!未找到指定用户,请引用对方消息再进行操作。"; 263 | return returnText; 264 | } 265 | 266 | if (getPermissionList(userJson).indexOf(userJson.from.id.toString()) == -1) { 267 | returnText = 268 | "Bot用户封禁功能仅开放于Bot管理者,请拉取最新版XiaoMaoBot代码部署后再试吧!"; 269 | return returnText; 270 | } else if (PermissionRelease && userJson.chat.type == "supergroup") { 271 | let payloadPostData = { 272 | method: "banChatMember", 273 | chat_id: userJson.reply_to_message.chat.id.toString(), 274 | user_id: userJson.reply_to_message.from.id.toString(), 275 | until_date: getUnixTime(timeFrame).toString(), 276 | }; 277 | 278 | try { 279 | linkBot({ 280 | method: "post", 281 | payload: payloadPostData, 282 | }); 283 | } catch (e) {} 284 | 285 | let payloadPostData2 = { 286 | method: "sendMessage", 287 | chat_id: userJson.reply_to_message.chat.id.toString(), 288 | text: 289 | "📣来自XiaoMaoBot管理员的违规提醒" + 290 | "\n" + 291 | "\n" + 292 | "===========================" + 293 | "\n" + 294 | "\n" + 295 | "" + 296 | payloadPostData.user_id + 297 | " 因存在违规行为,您已被管理员封禁(封禁时长:" + 298 | (timeFrame ? timeFrame : "永久") + 299 | "),申诉请私聊" + 300 | " XiaoMao机器人 " + 301 | "" + 302 | "\n" + 303 | "\n" + 304 | "===========================" + 305 | "\n", 306 | parse_mode: "HTML", 307 | reply_markup: JSON.stringify(keyboardFollowManageParams), 308 | disable_web_page_preview: true, 309 | }; 310 | linkBot({ 311 | method: "post", 312 | payload: payloadPostData2, 313 | }); 314 | deleteUserMessage(userJson, 2); 315 | 316 | return "操作成功!"; 317 | } else if (userJson.chat.type == "private") { 318 | if (!userJson.hasOwnProperty("reply_to_message")) { 319 | returnText = 320 | "未找到引用消息内容,Bot用户封禁功能需要开启私人消息推送服务,请于 XiaoMao_TgBot仓库 👈 中查看开启及使用方式。"; 321 | return returnText; 322 | } else { 323 | if ( 324 | userJson.reply_to_message.from.username != "Xiao_MaoMao_bot" && 325 | userJson.reply_to_message.from.is_bot != true && 326 | userJson.chat.type == "private" 327 | ) { 328 | returnText = "Bot用户封禁功能仅限于回复Bot端私聊消息喔!"; 329 | return returnText; 330 | } else { 331 | try { 332 | let payloadPostData = { 333 | method: "banChatMember", 334 | chat_id: "", 335 | user_id: "", 336 | until_date: getUnixTime(timeFrame).toString(), 337 | }; 338 | if (userJson.reply_to_message.text.indexOf("来自[群聊]")) { 339 | let textReply = userJson.reply_to_message.text; 340 | let sub_1 = textReply.indexOf("chat"); 341 | let sub_Text = textReply.substring(sub_1 + 6, sub_1 + 30); 342 | let sub_2 = sub_Text.indexOf(":"); 343 | let sub_3 = sub_Text.indexOf(","); 344 | let sub2_Text = sub_Text.substring(sub_2 + 1, sub_3); 345 | 346 | let sub_user_1 = textReply.indexOf('"id"'); 347 | let sub_user_Text = textReply.substring( 348 | sub_user_1 + 4, 349 | sub_user_1 + 30 350 | ); 351 | let sub_user_2 = sub_user_Text.indexOf(":"); 352 | let sub_user_3 = sub_user_Text.indexOf(","); 353 | let sub2_user_Text = sub_user_Text.substring( 354 | sub_user_2 + 1, 355 | sub_user_3 356 | ); 357 | payloadPostData.user_id = sub2_user_Text.toString(); 358 | payloadPostData.chat_id = sub2_Text.toString(); 359 | 360 | try { 361 | linkBot({ 362 | method: "post", 363 | payload: payloadPostData, 364 | }); 365 | } catch (e) {} 366 | 367 | let sub__1 = textReply.indexOf("chat"); 368 | let sub__Text = textReply.substring(sub__1 + 6, sub__1 + 30); 369 | let sub__2 = sub__Text.indexOf(":"); 370 | let sub__3 = sub__Text.indexOf(","); 371 | let sub2__Text = sub__Text.substring(sub__2 + 1, sub__3); 372 | 373 | let payloadPostData2 = { 374 | method: "sendMessage", 375 | chat_id: sub2__Text.toString(), 376 | text: 377 | "📣来自XiaoMaoBot管理员的违规提醒" + 378 | "\n" + 379 | "\n" + 380 | "===========================" + 381 | "\n" + 382 | "\n" + 383 | "" + 384 | payloadPostData.user_id + 385 | " 因存在违规行为,您已被管理员封禁(封禁时长:" + 386 | (timeFrame ? timeFrame : "永久") + 387 | "),申诉请私聊" + 388 | " XiaoMao机器人 " + 389 | "" + 390 | "\n" + 391 | "\n" + 392 | "===========================" + 393 | "\n", 394 | parse_mode: "HTML", 395 | reply_markup: JSON.stringify(keyboardFollowManageParams), 396 | disable_web_page_preview: true, 397 | }; 398 | linkBot({ 399 | method: "post", 400 | payload: payloadPostData2, 401 | }); 402 | 403 | deleteUserMessage(userJson); 404 | } else { 405 | returnText = "出错了,用户封禁功能仅支持来自群聊类型消息喔!"; 406 | return returnText; 407 | } 408 | return "✅ 用户 " + payloadPostData.user_id + "已被封禁"; 409 | } catch (e) { 410 | returnText = 411 | "出错了,请将以下错误码反馈给" + 412 | " XiaoMao机器人 " + 413 | "或" + 414 | "XiaoMao群聊管理员" + 415 | "\n\n" + 416 | e; 417 | return returnText; 418 | } 419 | } 420 | } 421 | } 422 | }; 423 | 424 | /** 425 | * 限制用户权限 426 | * @param userJson 427 | * @returns 428 | */ 429 | const getRestrictUser = (userJson) => { 430 | if (!userJson.hasOwnProperty("reply_to_message")) { 431 | returnText = "操作失败!未找到指定用户,请引用对方消息再进行操作。"; 432 | return returnText; 433 | } 434 | let permission = { 435 | can_send_messages: false, 436 | can_send_audios: false, 437 | can_send_documents: false, 438 | can_send_photos: false, 439 | can_send_videos: false, 440 | can_send_video_notes: false, 441 | can_send_voice_notes: false, 442 | can_send_polls: false, 443 | can_send_other_messages: false, 444 | can_add_web_page_previews: false, 445 | can_change_info: false, 446 | can_invite_users: false, 447 | can_pin_messages: false, 448 | can_manage_topics: false, 449 | }; 450 | let timeFrame = userJson.text.replace("/restrict", "") || ""; 451 | if (getPermissionList(userJson).indexOf(userJson.from.id.toString()) == -1) { 452 | returnText = 453 | "Bot用户限制功能仅开放于Bot管理者,请拉取最新版XiaoMaoBot代码部署后再试吧!"; 454 | return returnText; 455 | } else if (PermissionRelease && userJson.chat.type == "supergroup") { 456 | let payloadPostData = { 457 | method: "restrictChatMember", 458 | chat_id: userJson.reply_to_message.chat.id.toString(), 459 | user_id: userJson.reply_to_message.from.id.toString(), 460 | until_date: getUnixTime(timeFrame).toString(), 461 | permissions: JSON.stringify(permission), 462 | }; 463 | try { 464 | linkBot({ 465 | method: "post", 466 | payload: payloadPostData, 467 | }); 468 | } catch (e) {} 469 | 470 | let payloadPostData2 = { 471 | method: "sendMessage", 472 | chat_id: userJson.reply_to_message.chat.id.toString(), 473 | text: 474 | "📣来自XiaoMaoBot管理员的违规提醒" + 475 | "\n" + 476 | "\n" + 477 | "===========================" + 478 | "\n" + 479 | "\n" + 480 | "" + 481 | payloadPostData.user_id + 482 | " 因存在违规行为,您已被管理员限制聊天(限制时长:" + 483 | (timeFrame ? timeFrame : "永久") + 484 | "),申诉请私聊" + 485 | " XiaoMao机器人 " + 486 | "" + 487 | "\n" + 488 | "\n" + 489 | "===========================" + 490 | "\n", 491 | parse_mode: "HTML", 492 | reply_markup: JSON.stringify(keyboardFollowManageParams), 493 | disable_web_page_preview: true, 494 | }; 495 | linkBot({ 496 | method: "post", 497 | payload: payloadPostData2, 498 | }); 499 | deleteUserMessage(userJson, 2); 500 | 501 | return "操作成功!"; 502 | } else if (userJson.chat.type == "private") { 503 | if (!userJson.hasOwnProperty("reply_to_message")) { 504 | returnText = 505 | "未找到引用消息内容,Bot用户限制功能需要开启私人消息推送服务,请于 XiaoMao_TgBot仓库 👈 中查看开启及使用方式。"; 506 | return returnText; 507 | } else { 508 | if ( 509 | userJson.reply_to_message.from.username != "Xiao_MaoMao_bot" && 510 | userJson.reply_to_message.from.is_bot != true && 511 | userJson.chat.type == "private" 512 | ) { 513 | returnText = "Bot用户限制功能仅限于回复Bot端私聊消息喔!"; 514 | return returnText; 515 | } else { 516 | try { 517 | let payloadPostData = { 518 | method: "restrictChatMember", 519 | chat_id: "", 520 | user_id: "", 521 | until_date: getUnixTime(timeFrame).toString(), 522 | permissions: JSON.stringify(permission), 523 | }; 524 | if (userJson.reply_to_message.text.indexOf("来自[群聊]")) { 525 | let textReply = userJson.reply_to_message.text; 526 | let sub_1 = textReply.indexOf("chat"); 527 | let sub_Text = textReply.substring(sub_1 + 6, sub_1 + 30); 528 | let sub_2 = sub_Text.indexOf(":"); 529 | let sub_3 = sub_Text.indexOf(","); 530 | let sub2_Text = sub_Text.substring(sub_2 + 1, sub_3); 531 | 532 | let sub_user_1 = textReply.indexOf('"id"'); 533 | let sub_user_Text = textReply.substring( 534 | sub_user_1 + 4, 535 | sub_user_1 + 30 536 | ); 537 | let sub_user_2 = sub_user_Text.indexOf(":"); 538 | let sub_user_3 = sub_user_Text.indexOf(","); 539 | let sub2_user_Text = sub_user_Text.substring( 540 | sub_user_2 + 1, 541 | sub_user_3 542 | ); 543 | payloadPostData.user_id = sub2_user_Text.toString(); 544 | payloadPostData.chat_id = sub2_Text.toString(); 545 | 546 | try { 547 | linkBot({ 548 | method: "post", 549 | payload: payloadPostData, 550 | }); 551 | } catch (e) {} 552 | 553 | let sub__1 = textReply.indexOf("chat"); 554 | let sub__Text = textReply.substring(sub__1 + 6, sub__1 + 30); 555 | let sub__2 = sub__Text.indexOf(":"); 556 | let sub__3 = sub__Text.indexOf(","); 557 | let sub2__Text = sub__Text.substring(sub__2 + 1, sub__3); 558 | 559 | let payloadPostData2 = { 560 | method: "sendMessage", 561 | chat_id: sub2__Text.toString(), 562 | text: 563 | "📣来自XiaoMaoBot管理员的违规提醒" + 564 | "\n" + 565 | "\n" + 566 | "===========================" + 567 | "\n" + 568 | "\n" + 569 | "" + 570 | payloadPostData.user_id + 571 | " 因存在违规行为,您已被管理员限制聊天(限制时长:" + 572 | (timeFrame ? timeFrame : "永久") + 573 | "),申诉请私聊" + 574 | " XiaoMao机器人 " + 575 | "" + 576 | "\n" + 577 | "\n" + 578 | "===========================" + 579 | "\n", 580 | parse_mode: "HTML", 581 | reply_markup: JSON.stringify(keyboardFollowManageParams), 582 | disable_web_page_preview: true, 583 | }; 584 | linkBot({ 585 | method: "post", 586 | payload: payloadPostData2, 587 | }); 588 | deleteUserMessage(userJson); 589 | } else { 590 | returnText = "出错了,用户限制功能仅支持来自群聊类型消息喔!"; 591 | return returnText; 592 | } 593 | return "✅ 用户 " + payloadPostData.user_id + "已被限制"; 594 | } catch (e) { 595 | returnText = 596 | "出错了,请将以下错误码反馈给" + 597 | " XiaoMao机器人 " + 598 | "或" + 599 | "XiaoMao群聊管理员" + 600 | "\n\n" + 601 | e; 602 | return returnText; 603 | } 604 | } 605 | } 606 | } 607 | }; 608 | 609 | /** 610 | * 用于主人对私聊信息进行bot角色回复 611 | * @param userJson 612 | * @returns 613 | */ 614 | const getReply = (userJson) => { 615 | let followMessageKeyboard = [ 616 | [ 617 | { text: "✚ 频道", url: "https://t.me/xiaomaoJT" }, 618 | { text: "✚ 群聊", url: "https://t.me/hSuMjrQppKE5MWU9" }, 619 | { text: "✚ 脚本", url: "https://t.me/XiaoMaoScript" }, 620 | ], 621 | [{ text: "✚ 微信公众号『小帽集团』 ✚", callback_data: "WXGROUP" }], 622 | ]; 623 | let keyboardFollowParams = { 624 | inline_keyboard: followMessageKeyboard, 625 | }; 626 | let returnText = userJson.text.replace("/reply", "") || ""; 627 | if ( 628 | userJson.hasOwnProperty("chat") && 629 | userJson.from.id.toString() != KingId 630 | ) { 631 | returnText = 632 | "Bot消息私聊功能仅开放于Bot主人,请拉取最新版XiaoMaoBot代码部署后再试吧!"; 633 | return returnText; 634 | } else { 635 | if (!userJson.hasOwnProperty("reply_to_message")) { 636 | returnText = 637 | "未找到引用消息内容,Bot消息私聊功能需要开启私人消息推送服务,请于 XiaoMao_TgBot仓库 👈 中查看开启及使用方式。"; 638 | return returnText; 639 | } else { 640 | if ( 641 | userJson.reply_to_message.from.username != "Xiao_MaoMao_bot" && 642 | userJson.reply_to_message.from.is_bot != true && 643 | userJson.chat.type == "private" 644 | ) { 645 | returnText = "Bot消息私聊功能仅限于回复Bot端私聊消息喔!"; 646 | return returnText; 647 | } else { 648 | try { 649 | let payloadPostData = { 650 | method: "sendMessage", 651 | chat_id: userJson.from.id.toString(), 652 | text: 653 | "📣来自XiaoMaoBot管理员的主动回复" + 654 | "\n" + 655 | "\n" + 656 | "===========================" + 657 | "\n" + 658 | "\n" + 659 | "" + 660 | returnText + 661 | "" + 662 | "\n" + 663 | "\n" + 664 | "===========================" + 665 | "\n", 666 | parse_mode: "HTML", 667 | reply_markup: JSON.stringify(keyboardFollowParams), 668 | disable_web_page_preview: true, 669 | }; 670 | if (userJson.reply_to_message.text.indexOf("来自[群聊]")) { 671 | let textReply = userJson.reply_to_message.text; 672 | let sub1 = textReply.indexOf("message_id"); 673 | let subText = textReply.substring(sub1, sub1 + 30); 674 | let sub2 = subText.indexOf(":"); 675 | let sub3 = subText.indexOf(","); 676 | let sub2Text = subText.substring(sub2 + 1, sub3); 677 | 678 | let sub_1 = textReply.indexOf("chat"); 679 | let sub_Text = textReply.substring(sub_1 + 6, sub_1 + 30); 680 | let sub_2 = sub_Text.indexOf(":"); 681 | let sub_3 = sub_Text.indexOf(","); 682 | let sub2_Text = sub_Text.substring(sub_2 + 1, sub_3); 683 | payloadPostData.chat_id = sub2_Text.toString(); 684 | payloadPostData.reply_to_message_id = sub2Text.toString(); 685 | } 686 | 687 | linkBot({ 688 | method: "post", 689 | payload: payloadPostData, 690 | }); 691 | 692 | return "✅ 私聊信息已发送成功"; 693 | } catch (e) { 694 | returnText = 695 | "出错了,消息发送失败!当前版本仅可用于回复文字消息,请注意检查回复内容及引用消息出处!" + 696 | "请将以下错误码反馈给" + 697 | " XiaoMao机器人 " + 698 | "或" + 699 | "XiaoMao群聊管理员" + 700 | "\n\n" + 701 | e; 702 | return returnText; 703 | } 704 | } 705 | } 706 | } 707 | }; 708 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/MaoBot.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * author : @XiaoMao 3 | * # 小版本更新请查看更新日志 | 或加入xiaomao组织⬇️ 4 | * # 微信公众号 【小帽集团】 5 | * # XiaoMao · Tg频道频道:https://t.me/xiaomaoJT 6 | * 7 | * V1.11 - 正式版 8 | * 9 | * 核心函数 10 | * 无需改动 11 | * 12 | * Google App Script 13 | * 用于执行tg机器人功能 14 | * 功能描述:❶ 超级群管功能❷ 广告词/敏感词过滤、自动删除/警告❸ 多样化接口查询、XiaoMao数据加工❹ 自定义聊天窗快捷键盘/消息跟随按钮❺ 关键字消息/私聊消息 自动回复❻ 私聊消息/群组消息 捕捉及消息私人推送❼ 私聊消息/群组消息 自动存储 15 | * 16 | * 功能细则:入群检测、退群检测、入群欢迎、退群欢送、超级群管功能、用户封禁、用户解封、用户禁言、广告词敏感词拦截及自动删除、chatGPT查询、消息私人推送、BOT消息主动回复、自动接口查询及数据加工、自定义键盘、私聊及自动回复、关键字自动回复、消息存储等功能 17 | * 18 | * 源码开发不易,使用引用请注明出处! 19 | * 源码开发不易,使用引用请注明出处! 20 | * 源码开发不易,使用引用请注明出处! 21 | */ 22 | 23 | /** 24 | * 用于接收用户传来的讯息JSON 25 | * @param {*} e 26 | */ 27 | const doPost = async (e) => { 28 | // !!!!!仅用于数据结构展示,此段代码无效!!!!! 29 | if (e == undefined) { 30 | let testParams = { 31 | postData: { 32 | contents: `{"update_id":11111111,"message":{"message_id":2,"from":{"id":${parseInt( 33 | KingId 34 | )},"is_bot":false,"first_name":"","username":"","language_code":"zh-hans"},"chat":{"id":${parseInt( 35 | KingId 36 | )},"first_name":"","username":"","type":"private"},"date":1722500047,"text":"图文教程"}}`, 37 | }, 38 | }; 39 | console.log( 40 | "e参数示例:", 41 | testParams, 42 | "该示例仅用于数据结构展示,请勿用于生产!!!" 43 | ); 44 | console.error( 45 | "⚠️⚠️⚠️若正式环境执行出现此内容,则证明数据尚未接入成功,请核对教程第四步,https://api.telegram.org/bot『你的tg机器人Token』/setWebhook?url=『你的web应用网址』,注意删除多余空格!!" 46 | ); 47 | console.log( 48 | "【无法通过GAS直接执行问题】机器人通过检测到TG消息方才会响应,直接运行将使得入口函数doPost缺失关键参数而导致失败,若需直接执行,请于本地补全参数e,可作于调试运行。参数e的获取建议于部署完成后,通过私人推送服务获取原始数据。" 49 | ); 50 | // 仅用于测试,将下方 return 注释,即可点击运行查看机器人响应效果 51 | // 需提前私聊个人机器人 52 | return; 53 | e = testParams; 54 | } 55 | // !!!!!仅用于数据结构展示,此段代码无效!!!!! 56 | 57 | // 获取响应数据 必传 58 | let userMessage = JSON.parse(e.postData.contents); 59 | 60 | // 频道消息 61 | if (userMessage?.channel_post) { 62 | setStorage(userMessage, "CHANNELPOST"); 63 | return; 64 | } 65 | 66 | // 判断消息类型 67 | if (userMessage.hasOwnProperty("callback_query")) { 68 | MESSAGETYPE = 1; 69 | userMessage = JSON.parse(e.postData.contents).callback_query; 70 | } 71 | if (userMessage.message.hasOwnProperty("left_chat_participant")) { 72 | MESSAGETYPE = 3; 73 | } 74 | if (userMessage.message.hasOwnProperty("new_chat_participant")) { 75 | MESSAGETYPE = 2; 76 | } 77 | 78 | getCacheAuthorityList(); 79 | 80 | //计算返回式 81 | let messageUserID = 82 | userMessage.message.chat.type == "private" 83 | ? userMessage.message.from.id.toString() 84 | : userMessage.message.chat.id.toString(); 85 | 86 | MESSAGETYPE == 0 && userMessage.message.hasOwnProperty("text") 87 | ? (dealMessage = processReplyWord( 88 | userMessage.message.text, 89 | messageUserID, 90 | userMessage.message 91 | )) 92 | : ""; 93 | 94 | //回调响应逻辑 95 | let payload = processData(userMessage); 96 | let data = null; 97 | let payloadStatus = payload instanceof Array; 98 | if (payloadStatus) { 99 | data = []; 100 | payload.length 101 | ? payload.map((e) => { 102 | return data.push({ 103 | method: "post", 104 | payload: e, 105 | }); 106 | }) 107 | : ""; 108 | } else { 109 | data = { 110 | method: "post", 111 | payload: payload, 112 | }; 113 | } 114 | 115 | // 分析文字消息是否包含关键字 未包含将不做匹配 116 | let htmlReplyState = true; 117 | if (MESSAGETYPE == 0 && userMessage.message) { 118 | // 判断消息类型 - 进行私聊或群聊回复 119 | htmlReplyState = dealMessage.state; 120 | } 121 | // Google 请求域建立连接 122 | // 判断消息,仅对私聊和@消息以及关键字进行回复 123 | if ( 124 | (userMessage.message.reply_to_message && 125 | userMessage.message.reply_to_message.from.id == botIdAlone) || 126 | htmlReplyState || 127 | userMessage.message.chat.type == "private" || 128 | (userMessage.message.hasOwnProperty("entities") && 129 | userMessage.message.entities[0].type == "mention" && 130 | htmlReplyState) || 131 | (userMessage.message.hasOwnProperty("entities") && 132 | userMessage.message.entities[0].type == "bold") 133 | ) { 134 | if (payloadStatus) { 135 | if (data.length) { 136 | let list = getApiBackList(data); 137 | try { 138 | if (list.length && list[0][2] == "supergroup") { 139 | createDelayedTriggerWithParams(list); 140 | } 141 | } catch (error) {} 142 | } 143 | } else { 144 | linkBot(data); 145 | setStorage(data, "MESSAGEBACK"); 146 | } 147 | } 148 | }; 149 | 150 | /** 151 | * 普通账号上限20个 152 | * 删除指定触发器 index true 153 | * 删除多余触发器 false num 154 | */ 155 | const deleteClockTriggers = (index = 0,status = true,num = 0) => { 156 | // 获取所有时间类型触发器 157 | let allTriggers = ScriptApp.getProjectTriggers().filter( 158 | (t) => t.getTriggerSource() === ScriptApp.TriggerSource.CLOCK 159 | ); 160 | if(allTriggers.length && allTriggers.length > index){ 161 | if(status){ 162 | ScriptApp.deleteTrigger(allTriggers[index]) 163 | }else{ 164 | for (let i = 0; i < num; i++) { 165 | ScriptApp.deleteTrigger(allTriggers[i]) 166 | } 167 | } 168 | } 169 | }; 170 | 171 | /** 172 | * 普通账号上限20个 173 | * 查询触发器数量 - 大于19个立即循环执行消息删除函数 174 | * @returns 175 | */ 176 | const getClockTriggersNum = () => { 177 | // 获取所有时间类型触发器 178 | let allTriggers = ScriptApp.getProjectTriggers().filter( 179 | (t) => t.getTriggerSource() === ScriptApp.TriggerSource.CLOCK 180 | ); 181 | if(allTriggers.length >= 19){ 182 | let surplusIndex = allTriggers.length - 19 183 | cyclicDeleteTrigger(surplusIndex) 184 | } 185 | return allTriggers.length; 186 | }; 187 | 188 | /** 189 | * 循环执行多余触发器 190 | * @param num 191 | */ 192 | const cyclicDeleteTrigger = (num) => { 193 | for (let index = 0; index < num; index++) { 194 | executeAfterDelay() 195 | } 196 | } 197 | 198 | /** 199 | * 创建消息删除触发器 200 | * @param params botapi返回数据列表 201 | */ 202 | const createDelayedTriggerWithParams = (params) => { 203 | let list = {}; 204 | const scriptProperties = PropertiesService.getScriptProperties(); 205 | let paramsList = scriptProperties.getProperty("triggerParams"); 206 | if (paramsList) { 207 | list = JSON.parse(paramsList); 208 | let objKeyLength = Object.keys(list); 209 | if (objKeyLength.length) { 210 | let keyName = Object.keys(list).sort((a, b) => { 211 | return a - b; 212 | })[objKeyLength.length - 1]; 213 | list[parseInt(keyName) + 1] = params; 214 | } else { 215 | list = { 0: params }; 216 | } 217 | } else { 218 | list = { 0: params }; 219 | } 220 | scriptProperties.setProperty("triggerParams", JSON.stringify(list)); 221 | try { 222 | let surplusParamsIndex = Object.keys(list).length - getClockTriggersNum() 223 | if(surplusParamsIndex > 0){ 224 | cyclicDeleteTrigger(surplusParamsIndex) 225 | }else{ 226 | deleteClockTriggers(0,false,(surplusParamsIndex * -1) + 1) 227 | } 228 | const now = new Date(); 229 | const delayTime = new Date(now.getTime() + 30 * 1000); 230 | // 创建触发器 231 | ScriptApp.newTrigger("executeAfterDelay") 232 | .timeBased() 233 | .at(delayTime) 234 | .create(); 235 | } catch (e) {} 236 | }; 237 | 238 | /** 239 | * 消息触发器 240 | * @returns 241 | */ 242 | function executeAfterDelay() { 243 | const scriptProperties = PropertiesService.getScriptProperties(); 244 | const paramsString = scriptProperties.getProperty("triggerParams"); 245 | if (paramsString) { 246 | const params = JSON.parse(paramsString); 247 | let list = []; 248 | let objKeyLength = Object.keys(params); 249 | if (objKeyLength.length) { 250 | let keyName = Object.keys(params).sort((a, b) => { 251 | return a - b; 252 | })[0]; 253 | list = params[keyName]; 254 | try { 255 | list.map((listItem) => { 256 | if (listItem[2] == "supergroup") { 257 | deleteMessageApi(listItem[0], listItem[1]); 258 | } 259 | }); 260 | delete params[keyName]; 261 | deleteClockTriggers() 262 | scriptProperties.setProperty("triggerParams", JSON.stringify(params)); 263 | } catch (e) {} 264 | } else { 265 | return; 266 | } 267 | } else { 268 | return; 269 | } 270 | } 271 | 272 | /** 273 | * 用于处理用户信息并进行回复 274 | * @param {*} userMessage 275 | * @returns 276 | */ 277 | const processData = (userMessage) => { 278 | // 定义返回参数 279 | let payload; 280 | 281 | // 判断消息类型 - 进行私聊或群聊回复 282 | let messageUserID = 283 | userMessage.message.chat.type == "private" 284 | ? userMessage.message.from.id.toString() 285 | : userMessage.message.chat.id.toString(); 286 | let messageReplyID = userMessage.message.message_id.toString(); 287 | let messageNoType = userMessage.message.hasOwnProperty("text") 288 | ? userMessage.message.text 289 | : userMessage.message.hasOwnProperty("sticker") 290 | ? "[表情消息]" 291 | : userMessage.message.hasOwnProperty("photo") 292 | ? "[图片消息]" 293 | : userMessage.message.hasOwnProperty("video") 294 | ? "[视频消息]" 295 | : userMessage.message.hasOwnProperty("document") 296 | ? "[文件消息]" 297 | : userMessage.message.hasOwnProperty("voice") 298 | ? "[音频消息]" 299 | : "[消息]"; 300 | //默认回复 301 | let payloadPostData = { 302 | method: "sendMessage", 303 | chat_id: messageUserID, 304 | text: 305 | "🕹 来自XiaoMaoBot的消息:" + 306 | "\n" + 307 | "\n" + 308 | "呜呜呜,此类型 " + 309 | messageNoType + 310 | " 暂无法处理,XiaoMaoBot正在逐步升级中!可加入XiaoMao群聊咨询解决。", 311 | reply_to_message_id: messageReplyID, 312 | parse_mode: "HTML", 313 | reply_markup: JSON.stringify(keyboardFollowParams), 314 | disable_web_page_preview: true, 315 | }; 316 | 317 | // 获取图片fileID 318 | if ( 319 | userMessage.message.hasOwnProperty("photo") && 320 | userMessage.message.hasOwnProperty("caption") && 321 | userMessage.message.caption == "#photoid" 322 | ) { 323 | let photoList = userMessage.message.photo; 324 | if (photoList.length) { 325 | let photoText = 326 | "🕹 来自XiaoMaoBot的消息:" + 327 | "\n" + 328 | "\n" + 329 | `识别到图片存储指令,图片文件ID是\n\n${ 330 | photoList[photoList.length - 1].file_id 331 | }`; 332 | 333 | payloadPostData.text = 334 | photoText + 335 | `\n\n图片文件ID可用于填充关键字表[key_params]内[GraphicMessage]类型回复`; 336 | } 337 | } 338 | // 获取视频fileID 339 | if ( 340 | userMessage.message.hasOwnProperty("video") && 341 | userMessage.message.hasOwnProperty("caption") && 342 | userMessage.message.caption == "#videoid" 343 | ) { 344 | let videoObj = userMessage.message.video; 345 | let videoText = 346 | "🕹 来自XiaoMaoBot的消息:" + 347 | "\n" + 348 | "\n" + 349 | `识别到视频存储指令,视频文件ID是\n\n${videoObj.file_id}`; 350 | 351 | payloadPostData.text = 352 | videoText + 353 | `\n\n视频文件ID可用于填充关键字表[key_params]内[VideoMessage]类型回复`; 354 | } 355 | 356 | //判断消息类型 - 消息跟踪键盘 callback返回 357 | if (MESSAGETYPE == 1) { 358 | let callbackChatID = userMessage.message.chat.id.toFixed(); 359 | let payloadCallback; 360 | 361 | if (userMessage.data == "WXGROUP") { 362 | linkBot({ 363 | method: "post", 364 | payload: { 365 | method: "sendPhoto", 366 | chat_id: callbackChatID, 367 | photo: 368 | "https://mmbiz.qpic.cn/mmbiz_jpg/RzNtrrcUJxlEcDQkiasYkNhwN60JMqGhZyvzM6ZUIODsvAXaaohmySWuPfFic2FK7Q8SRdUvIHAgbzp0yBLagGqg/640?wx_fmt=jpeg&wxfrom=5&wx_lazy=1&wx_co=1", 369 | }, 370 | }); 371 | 372 | let callbackText = 373 | "🕹 来自XiaoMaoBot的消息:" + 374 | "\n" + 375 | "\n" + 376 | "✅微信公众号『小帽集团』,欢迎您的关注!记得点赞收藏哟~" + 377 | "\n" + 378 | "\n" + 379 | "XiaoMao推文集:" + 380 | "点击查看 👈"; 381 | 382 | payloadCallback = { 383 | method: "sendMessage", 384 | chat_id: callbackChatID, 385 | text: callbackText, 386 | parse_mode: "HTML", 387 | reply_markup: JSON.stringify(keyboardFollowParams), 388 | }; 389 | } 390 | payload = payloadCallback; 391 | setStorage(userMessage, "CALLBACK"); 392 | return payload; 393 | } 394 | 395 | if (MESSAGETYPE == 2 || MESSAGETYPE == 3) { 396 | let newMemberChatId = userMessage.message.chat.id.toString(); 397 | let memberList = "「未知」"; 398 | 399 | try { 400 | if (MESSAGETYPE == 2) { 401 | memberList = ""; 402 | userMessage.message["new_chat_members"].forEach((name, index) => { 403 | memberList = 404 | memberList + 405 | (name.first_name || "") + 406 | (name.last_name || "") + 407 | (index < userMessage.message["new_chat_members"].length - 1 408 | ? " 、 " 409 | : " "); 410 | }); 411 | } else { 412 | memberList = 413 | (userMessage.message["left_chat_member"].first_name || "") + 414 | (userMessage.message["left_chat_member"].last_name || ""); 415 | } 416 | } catch (e) {} 417 | 418 | let welcomeMessage = 419 | "🕹 来自XiaoMaoBot的消息:" + 420 | "\n" + 421 | "\n" + 422 | "👏👏👏 热烈欢迎小伙伴 " + 423 | memberList + 424 | " 的到来,入群不能水经验,但可以求罩!"; 425 | 426 | let leftMessage = 427 | "🕹 来自XiaoMaoBot的消息:" + 428 | "\n" + 429 | "\n" + 430 | "😩😩😩 幺儿啊 " + 431 | memberList + 432 | " 这么好玩的群都退了,你能上哪去?"; 433 | 434 | let newMemberPayload = { 435 | method: "sendMessage", 436 | chat_id: newMemberChatId, 437 | text: MESSAGETYPE == 2 ? welcomeMessage : leftMessage, 438 | parse_mode: "HTML", 439 | reply_markup: JSON.stringify(keyboardFollowParams), 440 | disable_web_page_preview: true, 441 | }; 442 | payload = newMemberPayload; 443 | 444 | return payload; 445 | } 446 | 447 | //判断消息类型 - 文本消息 448 | // 暂时只识别文本类消息 449 | try { 450 | if (userMessage.message && userMessage.message.hasOwnProperty("text")) { 451 | if (dealMessage.htmlReply) { 452 | let HTML_REPLY = 453 | dealMessage.htmlReply == "getTgId" 454 | ? "🕹 来自XiaoMaoBot的消息:" + 455 | "\n" + 456 | "\n" + 457 | "你的Tg_Chat_ID : " + 458 | "" + 459 | userMessage.message.from.id.toString() + 460 | "" 461 | : dealMessage.htmlReply; 462 | 463 | let dealMessageParseMode = dealMessage.hasOwnProperty("parseMode") 464 | ? dealMessage.parseMode 465 | : "HTML"; 466 | if (dealMessage.htmlReply2 == null) { 467 | if (dealMessageParseMode == "GraphicMessage") { 468 | payloadPostData = { 469 | method: "sendMediaGroup", 470 | chat_id: messageUserID, 471 | media: HTML_REPLY.replace( 472 | "🕹 来自XiaoMaoBot的消息:\n\n", 473 | "" 474 | ), 475 | }; 476 | } else { 477 | payloadPostData = { 478 | method: "sendMessage", 479 | chat_id: messageUserID, 480 | text: 481 | dealMessageParseMode !== "HTML" 482 | ? HTML_REPLY.replace(/<\/?[^>]+(>|$)/g, "*") 483 | : HTML_REPLY, 484 | reply_to_message_id: messageReplyID, 485 | parse_mode: dealMessageParseMode, 486 | reply_markup: JSON.stringify(keyboardParams), 487 | disable_web_page_preview: true, 488 | }; 489 | } 490 | } else { 491 | payloadPostData = [ 492 | { 493 | method: "sendMessage", 494 | chat_id: messageUserID, 495 | text: 496 | dealMessageParseMode !== "HTML" 497 | ? HTML_REPLY.replace(/<\/?[^>]+(>|$)/g, "*") 498 | : HTML_REPLY, 499 | reply_to_message_id: messageReplyID, 500 | parse_mode: dealMessageParseMode, 501 | reply_markup: JSON.stringify(keyboardParams), 502 | disable_web_page_preview: true, 503 | }, 504 | ]; 505 | dealMessage.htmlReply2.length 506 | ? dealMessage.htmlReply2.map((e) => { 507 | return payloadPostData.push({ 508 | method: "sendMessage", 509 | chat_id: messageUserID, 510 | text: 511 | dealMessageParseMode !== "HTML" 512 | ? e.replace(/<\/?[^>]+(>|$)/g, "*") 513 | : e, 514 | reply_to_message_id: messageReplyID, 515 | parse_mode: dealMessageParseMode, 516 | reply_markup: JSON.stringify(keyboardParams), 517 | disable_web_page_preview: true, 518 | }); 519 | }) 520 | : ""; 521 | } 522 | } else { 523 | payloadPostData = { 524 | method: "deleteMessage", 525 | chat_id: userMessage.message.chat.id.toString(), 526 | message_id: userMessage.message.message_id.toString(), 527 | }; 528 | let htmlReply = 529 | "🕹 来自XiaoMaoBot的消息:" + 530 | "\n" + 531 | "\n" + 532 | "拦截到 " + 533 | " " + 534 | (userMessage.message.from.first_name || "") + 535 | (userMessage.message.from.last_name || "") + 536 | " 消息中含" + 537 | dealMessage.dfa.wordLength + 538 | "处 敏感词,XiaoMao已自动删除消息,请文明聊天喔!"; 539 | let payload = { 540 | method: "sendMessage", 541 | chat_id: messageUserID, 542 | text: htmlReply, 543 | reply_to_message_id: messageReplyID, 544 | parse_mode: "HTML", 545 | reply_markup: JSON.stringify(keyboardParams), 546 | disable_web_page_preview: true, 547 | }; 548 | 549 | linkBot({ 550 | method: "post", 551 | payload: payload, 552 | }); 553 | 554 | //强杀广告 - 直接ban 555 | let banKeyWords = 556 | sensitiveEncodeList 557 | .slice(0, banKeyLastIndex) 558 | .map((word) => 559 | Utilities.newBlob(Utilities.base64Decode(word)).getDataAsString() 560 | ) || []; 561 | function judgeBanStatus(banStauts = false) { 562 | for (i in banKeyWords) { 563 | if (userMessage.message.text.includes(banKeyWords[i])) { 564 | banStauts = true; 565 | break; 566 | } 567 | } 568 | return banStauts; 569 | } 570 | 571 | if (judgeBanStatus()) { 572 | let banPostData = { 573 | method: "banChatMember", 574 | chat_id: userMessage.message.chat.id.toString(), 575 | user_id: userMessage.message.from.id.toString(), 576 | until_date: getUnixTime("").toString(), 577 | }; 578 | try { 579 | linkBot({ 580 | method: "post", 581 | payload: banPostData, 582 | }); 583 | 584 | let payloadPostData2 = { 585 | method: "sendMessage", 586 | chat_id: userMessage.message.chat.id.toString(), 587 | text: 588 | "🚨XiaoMao绝杀通知" + 589 | "\n" + 590 | "\n" + 591 | "\n" + 592 | "===========================" + 593 | "\n" + 594 | "\n" + 595 | "" + 596 | userMessage.message.from.id.toString() + 597 | " 触发终极禁忌‼️ ,已被永久封禁,领盒饭吧狗子🐶~" + 598 | "" + 599 | "\n" + 600 | "\n" + 601 | "===========================" + 602 | "\n", 603 | parse_mode: "HTML", 604 | reply_markup: JSON.stringify(keyboardParams), 605 | disable_web_page_preview: true, 606 | }; 607 | linkBot({ 608 | method: "post", 609 | payload: payloadPostData2, 610 | }); 611 | } catch (e) {} 612 | } 613 | } 614 | 615 | if ( 616 | userMessage.message.text == "资源仓库" || 617 | userMessage.message.text.indexOf("Mao") != -1 618 | ) { 619 | payloadPostData.reply_markup = JSON.stringify( 620 | resourceWarehouseKeyboardFollowParams 621 | ); 622 | } 623 | 624 | if (userMessage.message.text == "微信公众号『小帽集团』") { 625 | payloadPostData.reply_markup = JSON.stringify(keyboardParams); 626 | } 627 | } 628 | } catch (error) { 629 | if (userMessage.message.chat.type == "private") { 630 | linkBot({ 631 | method: "post", 632 | payload: payloadPostData, 633 | }); 634 | } 635 | } 636 | 637 | payload = payloadPostData; 638 | setStorage(userMessage, "POSTDATA"); 639 | 640 | pushDataToKing(userMessage); 641 | return payload; 642 | }; 643 | 644 | /** 645 | * 用于处理用户关键字自动回复 646 | * keyword值唯一不可重复,用于匹配用户关键字是否包含,并触发自动回复 647 | * @param key 用户消息关键字 648 | */ 649 | const processReplyWord = (key, useId, userJson) => { 650 | getCacheData(); 651 | //未匹配的关键字回复 652 | let htmlReply = 653 | "🕹 来自XiaoMaoBot的消息:" + 654 | "\n" + 655 | "\n" + 656 | "呜呜呜,关键字 " + 657 | key.replace("@Xiao_MaoMao_bot", "") + 658 | " 匹配失败,XiaoMao已采集,正在抓紧学习!"; 659 | if (outsideWord.findIndex((i) => key == i) != -1) { 660 | htmlReply = 661 | "🕹 来自XiaoMaoBot的消息:" + 662 | "\n" + 663 | "\n" + 664 | "✅微信公众号『小帽集团』,欢迎您的关注!记得点赞收藏哟~" + 665 | "\n" + 666 | "\n" + 667 | "XiaoMao推文集:" + 668 | "点击查看 👈"; 669 | returnHtmlReply.state = true; 670 | } else { 671 | let dfa = checkSensitiveDFA(key); 672 | if (dfa.wordLength > 0) { 673 | returnHtmlReply.dfa = dfa; 674 | returnHtmlReply.htmlReply = null; 675 | returnHtmlReply.state = true; 676 | return returnHtmlReply; 677 | } 678 | if (isApi(commandWord, key).status) { 679 | switch (isApi(commandWord, key).id) { 680 | case 0: 681 | apiReply(useId, userJson); 682 | htmlReply = 683 | "🕹 来自XiaoMaoBot的消息:" + 684 | "\n" + 685 | "\n" + 686 | getWeatherApi(getString(key, isApi(commandWord, key).api)); 687 | returnHtmlReply.state = true; 688 | break; 689 | case 1: 690 | apiReply(useId, userJson); 691 | htmlReply = 692 | "🕹 来自XiaoMaoBot的消息:" + 693 | "\n" + 694 | "\n" + 695 | getLinkShort(getString(key, isApi(commandWord, key).api)); 696 | returnHtmlReply.state = true; 697 | break; 698 | case 2: 699 | apiReply(useId, userJson); 700 | htmlReply = 701 | "🕹 来自XiaoMaoBot的消息:" + "\n" + "\n" + getMusic(); 702 | returnHtmlReply.state = true; 703 | break; 704 | case 3: 705 | apiReply(useId, userJson); 706 | htmlReply = 707 | "🕹 来自XiaoMaoBot的消息:" + 708 | "\n" + 709 | "\n" + 710 | getPhoneWhere(getString(key, isApi(commandWord, key).api)); 711 | returnHtmlReply.state = true; 712 | break; 713 | case 4: 714 | apiReply(useId, userJson); 715 | htmlReply = 716 | "🕹 来自XiaoMaoBot的消息:" + 717 | "\n" + 718 | "\n" + 719 | getTianGou(getString(key, isApi(commandWord, key).api)); 720 | returnHtmlReply.state = true; 721 | break; 722 | case 5: 723 | apiReply(useId, userJson); 724 | htmlReply = 725 | "🕹 来自XiaoMaoBot的消息:" + 726 | "\n" + 727 | "\n" + 728 | getDuJiTang(getString(key, isApi(commandWord, key).api)); 729 | returnHtmlReply.state = true; 730 | break; 731 | case 6: 732 | apiReply(useId, userJson); 733 | htmlReply = 734 | "🕹 来自XiaoMaoBot的消息:" + 735 | "\n" + 736 | "\n" + 737 | getVideo(getString(key, isApi(commandWord, key).api)); 738 | returnHtmlReply.state = true; 739 | 740 | break; 741 | case 7: 742 | apiReply(useId, userJson); 743 | htmlReply = 744 | "🕹 来自XiaoMaoBot的消息:" + "\n" + "\n" + getYiYan(); 745 | returnHtmlReply.state = true; 746 | break; 747 | case 8: 748 | apiReply(useId, userJson); 749 | htmlReply = 750 | "🕹 来自XiaoMaoBot的消息:" + 751 | "\n" + 752 | "\n" + 753 | getHelloBot(getString(key, isApi(commandWord, key).api)); 754 | returnHtmlReply.state = true; 755 | break; 756 | case 9: 757 | apiReply(useId, userJson); 758 | htmlReply = 759 | "🕹 来自XiaoMaoBot的消息:" + 760 | "\n" + 761 | "\n" + 762 | getChatBot(getString(key, isApi(commandWord, key).api)); 763 | returnHtmlReply.state = true; 764 | break; 765 | case 10: 766 | htmlReply = "getTgId"; 767 | returnHtmlReply.state = true; 768 | break; 769 | case 11: 770 | htmlReply = 771 | "🕹 来自XiaoMaoBot的消息:" + 772 | "\n" + 773 | "\n" + 774 | "Hello,我是 XiaoMao机器人,很高兴认识您!我能较出色的完成以下功能:" + 775 | "\n" + 776 | "\n" + 777 | "❶ 超级群管功能(/manage)" + 778 | "\n" + 779 | "❷ 广告词/敏感词过滤、自动删除/警告" + 780 | "\n" + 781 | "❸ 多样化接口查询、XiaoMao数据加工" + 782 | "\n" + 783 | "❹ 自定义聊天窗快捷键盘/消息跟随按钮" + 784 | "\n" + 785 | "❺ 关键字消息/私聊消息 自动回复" + 786 | "\n" + 787 | "❻ 私聊消息/群组消息 捕捉及消息私人推送" + 788 | "\n" + 789 | "❼ 私聊消息/群组消息 自动存储" + 790 | "\n" + 791 | "\n" + 792 | "🉑️通过底部按钮 【 资源仓库 】 加入XiaoMao组织喔~" + 793 | "\n" + 794 | "\n" + 795 | "🏖 本机器人完全开源,可点击查看我的源码仓库获取免费搭建教程喔!"; 796 | returnHtmlReply.state = true; 797 | break; 798 | case 12: 799 | apiReply(useId, userJson); 800 | htmlReply = 801 | "🕹 来自XiaoMaoBot的消息:" + 802 | "\n" + 803 | "\n" + 804 | getLanLink(getString(key, isApi(commandWord, key).api)); 805 | returnHtmlReply.state = true; 806 | break; 807 | case 13: 808 | apiReply(useId, userJson); 809 | htmlReply = 810 | "🕹 来自XiaoMaoBot的消息:" + 811 | "\n" + 812 | "\n" + 813 | getSao(getString(key, isApi(commandWord, key).api)); 814 | returnHtmlReply.state = true; 815 | break; 816 | case 14: 817 | htmlReply = 818 | "🕹 来自XiaoMaoBot的消息:" + 819 | "\n" + 820 | "\n" + 821 | getReply(userJson); 822 | returnHtmlReply.state = true; 823 | break; 824 | case 15: 825 | htmlReply = 826 | "🕹 来自XiaoMaoBot的消息:" + 827 | "\n" + 828 | "\n" + 829 | getBanUser(userJson); 830 | returnHtmlReply.state = true; 831 | break; 832 | case 16: 833 | htmlReply = 834 | "🕹 来自XiaoMaoBot的消息:" + 835 | "\n" + 836 | "\n" + 837 | getUnBanUser(userJson); 838 | returnHtmlReply.state = true; 839 | break; 840 | case 17: 841 | htmlReply = 842 | "🕹 来自XiaoMaoBot的消息:" + 843 | "\n" + 844 | "\n" + 845 | getRestrictUser(userJson); 846 | returnHtmlReply.state = true; 847 | break; 848 | case 18: 849 | apiReply(useId, userJson); 850 | htmlReply = 851 | "🕹 来自XiaoMaoBot的消息:" + 852 | "\n" + 853 | "\n" + 854 | getHotList(getString(key, isApi(commandWord, key).api)); 855 | returnHtmlReply.state = true; 856 | break; 857 | case 19: 858 | apiReply(useId, userJson); 859 | htmlReply = 860 | "🕹 来自XiaoMaoBot的消息:" + 861 | "\n" + 862 | "\n" + 863 | getDouBan(getString(key, isApi(commandWord, key).api)); 864 | returnHtmlReply.state = true; 865 | break; 866 | case 20: 867 | apiReply(useId, userJson); 868 | htmlReply = 869 | "🕹 来自XiaoMaoBot的消息:" + 870 | "\n" + 871 | "\n" + 872 | getHoroscopeList(getString(key, isApi(commandWord, key).api)); 873 | returnHtmlReply.state = true; 874 | break; 875 | case 21: 876 | htmlReply = 877 | "🕹 来自XiaoMaoBot的消息:" + 878 | "\n" + 879 | "\n" + 880 | getChatterboxUser(useId, userJson); 881 | returnHtmlReply.state = true; 882 | break; 883 | default: 884 | returnHtmlReply.state = false; 885 | break; 886 | } 887 | } else { 888 | //关键字匹配 若匹配失败自动进入hello机器人 889 | try { 890 | autoReply.forEach((item) => { 891 | item.keyword.forEach((element) => { 892 | if (key.indexOf(element) != -1) { 893 | htmlReply = 894 | "🕹 来自XiaoMaoBot的消息:" + 895 | "\n" + 896 | "\n" + 897 | item.replyWord; 898 | 899 | item.hasOwnProperty("replyWordMore") && item.replyWordMore.length 900 | ? (returnHtmlReply.htmlReply2 = item.replyWordMore) 901 | : (returnHtmlReply.htmlReply2 = null); 902 | returnHtmlReply.state = true; 903 | returnHtmlReply.parseMode = item.hasOwnProperty("parseMode") 904 | ? item.parseMode 905 | : "HTML"; 906 | throw new Error("匹配成功"); 907 | } 908 | }); 909 | }); 910 | if ( 911 | userJson && 912 | userJson.reply_to_message && 913 | userJson.reply_to_message.from.id == botIdAlone 914 | ) { 915 | htmlReply = 916 | "🕹 来自XiaoMaoBot的消息:" + "\n" + "\n" + getHelloBot(key); 917 | returnHtmlReply.state = true; 918 | } 919 | } catch (e) {} 920 | } 921 | } 922 | 923 | returnHtmlReply.htmlReply = htmlReply; 924 | 925 | return returnHtmlReply; 926 | }; 927 | 928 | /** 929 | * 用于捕捉机器人信息 930 | * @param key 用户消息 931 | * 当KingId未填写时,私人推送将不执行 932 | */ 933 | const pushDataToKing = (key) => { 934 | if ( 935 | KingType == 2 && 936 | KingId != "" && 937 | (key.message.chat.type == "private" || 938 | userMessage.message.chat.type == "supergroup") 939 | ) { 940 | } else if ( 941 | KingType == 3 && 942 | KingId != "" && 943 | key.message.chat.type == "private" 944 | ) { 945 | } else if ( 946 | KingType == 4 && 947 | KingId != "" && 948 | key.message.chat.type == "supergroup" 949 | ) { 950 | } else if (KingType == 1 && KingId != "") { 951 | } else { 952 | return; 953 | } 954 | 955 | if ( 956 | KingId == key.message.from.id.toString() && 957 | key.message.chat.type == "private" 958 | ) { 959 | return; 960 | } 961 | 962 | if (forGotList.indexOf(key.message.chat.id.toString()) != -1) { 963 | return; 964 | } 965 | let userMessage = key; 966 | let MessageUrl = 967 | userMessage.message.chat.type == "private" 968 | ? null 969 | : "https://t.me/" + 970 | userMessage.message.chat.username + 971 | "/" + 972 | userMessage.message.message_id; 973 | let MessageUseUrl = "https://t.me/" + userMessage.message.from.username; 974 | let messageInfoType = userMessage.message.hasOwnProperty("text") 975 | ? "[文本消息] " + userMessage.message.text 976 | : userMessage.message.hasOwnProperty("sticker") 977 | ? "[表情消息]" 978 | : userMessage.message.hasOwnProperty("photo") 979 | ? "[图片消息]" 980 | : userMessage.message.hasOwnProperty("video") 981 | ? "[视频消息]" 982 | : userMessage.message.hasOwnProperty("document") 983 | ? "[文件消息]" 984 | : userMessage.message.hasOwnProperty("voice") 985 | ? "[音频消息]" 986 | : "[未知消息类型]"; 987 | //用于捕捉机器人信息 988 | let messageToKing = 989 | "🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄" + 990 | "\n" + 991 | "\n" + 992 | "XiaoMaoBot捕捉到用户讯息" + 993 | "\n" + 994 | "\n" + 995 | "📝 简要内容:" + 996 | messageInfoType.replace(/\n/g, " ").substring(0, 100) + 997 | (messageInfoType.length > 100 ? "..." : "") + 998 | "\n" + 999 | "🎎 原始用户:" + 1000 | "" + 1003 | (userMessage.message.from.first_name != undefined 1004 | ? userMessage.message.from.first_name 1005 | : "") + 1006 | (userMessage.message.from.last_name != undefined 1007 | ? userMessage.message.from.last_name 1008 | : "") + 1009 | "" + 1010 | "\n" + 1011 | "🏖 来源位置:" + 1012 | (userMessage.message.chat.type == "private" 1013 | ? "来自 " + "[私聊]" 1014 | : userMessage.message.chat.hasOwnProperty("username") 1015 | ? "" + 1018 | "来自" + 1019 | (userMessage.message.chat.type == "supergroup" 1020 | ? "[群聊] " + userMessage.message.chat.title 1021 | : "[未知]") + 1022 | "" 1023 | : "来自" + 1024 | (userMessage.message.chat.type == "supergroup" 1025 | ? "[私人群聊] " + userMessage.message.chat.title 1026 | : "[未知]")) + 1027 | "\n" + 1028 | "🛎 发送时间:" + 1029 | getNowDate() + 1030 | "\n" + 1031 | "📰 原始数据:" + 1032 | "\n" + 1033 | JSON.stringify(userMessage) + 1034 | "\n" + 1035 | "\n" + 1036 | "🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀"; 1037 | 1038 | let dataKing = { 1039 | method: "post", 1040 | payload: {}, 1041 | }; 1042 | dataKing.payload = { 1043 | method: "sendMessage", 1044 | chat_id: KingId, 1045 | text: messageToKing, 1046 | parse_mode: "HTML", 1047 | disable_web_page_preview: true, 1048 | }; 1049 | 1050 | linkBot(dataKing); 1051 | 1052 | if (KingInfo) { 1053 | let dataKingInfo = { 1054 | method: "post", 1055 | payload: { 1056 | method: "", 1057 | chat_id: KingId, 1058 | }, 1059 | }; 1060 | userMessage.message.hasOwnProperty("caption") 1061 | ? (dataKingInfo.payload.caption = userMessage.message.caption) 1062 | : ""; 1063 | if (messageInfoType == "[表情消息]") { 1064 | dataKingInfo.payload.method = "sendSticker"; 1065 | dataKingInfo.payload.sticker = userMessage.message.sticker.file_id; 1066 | } else if (messageInfoType == "[图片消息]") { 1067 | dataKingInfo.payload.method = "sendPhoto"; 1068 | dataKingInfo.payload.photo = userMessage.message.photo[0].file_id; 1069 | } else if (messageInfoType == "[视频消息]") { 1070 | dataKingInfo.payload.method = "sendVideo"; 1071 | dataKingInfo.payload.video = userMessage.message.video.file_id; 1072 | } else if (messageInfoType == "[文件消息]") { 1073 | dataKingInfo.payload.method = "sendDocument"; 1074 | dataKingInfo.payload.document = userMessage.message.document.file_id; 1075 | } else if (messageInfoType == "[音频消息]") { 1076 | dataKingInfo.payload.method = "sendVoice"; 1077 | dataKingInfo.payload.voice = userMessage.message.voice.file_id; 1078 | } else { 1079 | return; 1080 | } 1081 | 1082 | linkBot(dataKingInfo); 1083 | } 1084 | }; 1085 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/Params.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 参数类 3 | * 4 | * 需手动补全的参数 EXECID 、 BOTID 、KingId 、botIdAlone 5 | * 6 | * 用于预定义XiaoMaoBot所需参数 7 | */ 8 | 9 | /** 10 | * ·⚠️必填 11 | * ·⚠️必填 12 | * ·⚠️必填 13 | * +++++++++ 预定义参数·请补充对应内容 +++++++++ 14 | */ 15 | 16 | // Google EXEC ID - 谷歌表格ID 17 | var EXECID = ""; 18 | // Telegram BOT ID key - tg机器人Token 19 | var BOTID = ""; 20 | 21 | // Google EXEC ID - 谷歌表格 - 数据存储工作表名 22 | var EXECNAME = "db_telegram"; 23 | // Google EXEC ID - 谷歌表格 - 关键字回复工作表名 24 | var KEYPARAMS = "key_params"; 25 | // Google EXEC ID - 谷歌表格 - 权限表工作表名 26 | var AUTHORITYMANAGEMENT = "authority_management"; 27 | 28 | 29 | /** 30 | * ·建议补全 31 | * ·建议补全 32 | * ·建议补全 33 | * +++++++++ 自定义参数·请按需修改参数·引号内留空此功能失效 +++++++++ 34 | * +++++++++ 为保证完整体验或奇怪的BUG,建议补全KingId/botIdAlone +++++++++ 35 | */ 36 | 37 | // 用于推送主人消息 取主人tg id - 私人消息主动功能必须填写此项 38 | var KingId = ""; 39 | //取 bot id 用于识别引用消息 40 | var botIdAlone = ""; 41 | 42 | // 1 全部类型 | 2 群聊 + 私聊类型 | 3 私聊类型 | 4 群聊类型 | 5 关闭 43 | var KingType = 1; 44 | // 1 推送详情(原图片、视频、音频、贴纸等)| 0 仅推送基础消息 45 | var KingInfo = 1; 46 | // 权限释放 - 用于开放操作权限给管理员 | 不释放为false 47 | var PermissionRelease = true; 48 | // 缓存过期时间(秒)- 180分钟 49 | var cacheExpirationInSeconds = 180 * 60; 50 | // 是否强制缓存刷新 -- 启用后将无视缓存实时获取数据 51 | var cacheExpirationStatus = true; 52 | 53 | /** 54 | * +++++++++ 系统默认通用参数·无需改动 +++++++++ 55 | */ 56 | 57 | // 管理员ID列表 58 | var PermissionReleaseList = []; 59 | // 用于过滤需要排除捕捉的群组信息 60 | var forGotList = []; 61 | // 用于判断消息类型 - inlinekey board回调 or 主动消息 62 | // 1 callback | 2 new member | 3 left member 63 | var MESSAGETYPE = 0; 64 | // 用于承接返回数据 65 | var dealMessage = {}; 66 | // 强ban关键字截止位 67 | var banKeyLastIndex = 21; 68 | // 敏感词库 -- 内容已作加密处理base64 69 | var sensitiveEncodeList = [ 70 | "5Luj5byA5Lya5ZGY", 71 | "5Luj5Yi3", 72 | "576k5Y+R6L2v5Lu2", 73 | "5bm/5ZGK5Luj5Y+R", 74 | "55yL5oiR566A5LuL", 75 | "5ouN6L2m6L6G6L+d5YGc", 76 | "5ouN54Wn54mH5pel5YWl", 77 | "54K55Ye75a+55o6l", 78 | "5oOz5YGa55qE5p2l", 79 | "5LiA5pel5YWr5L2w", 80 | "5oub5Lq65aSE55CG5pWw5o2u", 81 | "5pel5YWl6L+H5LiH", 82 | "6Z2g6LCx6aG555uu", 83 | "5aSE55CG5aSn6YeP5pWw5o2u", 84 | "6ZW/5pyf5oub5Lq6", 85 | "6aKE5LuY", 86 | "6ZyA6KaB5bel5L2c", 87 | "5pW055CG5pWw5o2u", 88 | "5aSE55CG6LWE5paZ", 89 | "5oOz5YGa55qE6IGU57O7", 90 | "5pyJ5rKh5pyJ5Lq65bmy5rS7", 91 | "5Yuk5b+r55qE5p2l", 92 | "5qOL54mM", 93 | "5b2p56Wo", 94 | "55yf5Lq6", 95 | "5pON5aWz", 96 | "5pON5aW5", 97 | "5pON5LuW", 98 | "5Yqg5b6u", 99 | "5YqgVg==", 100 | "5Yqgdg==", 101 | "5Lq65YW9", 102 | "5Lmx5Lym", 103 | "5Lmz5rKf", 104 | "5YW95Lqk", 105 | "5Y2W5q+U", 106 | "5Y2W6YC8", 107 | "5Y+X5a2V", 108 | "5bCE57K+", 109 | "5aW45rer", 110 | "5aaI6YC8", 111 | "5aaT5aWz", 112 | "5aiH5ZaY", 113 | "5amK5a2Q", 114 | "5aqa5aaZ", 115 | "5byA6Iue", 116 | "5oCn5Lqk", 117 | "5oCn5aW0", 118 | "5oCn5qyy", 119 | "5oCn54ix", 120 | "5oCn6JmQ5b6F", 121 | "5oOF6Imy", 122 | "5aupYg==", 123 | "5aupQg==", 124 | "5rer5Lmx", 125 | "5rer5aaH", 126 | "6I2h5aaH", 127 | "6IKb5Lqk", 128 | "57K+5ray", 129 | "54OC5q+U", 130 | "54OC6YC8", 131 | "6IKJ5qOS", 132 | "6IKJ57yd", 133 | "6IKP", 134 | "5aSn6bih5be0", 135 | "5aSn6Zue5be0", 136 | "57qm54Ku", 137 | "5pON5q+U", 138 | "5pON6YC8", 139 | "6Zi06IyO", 140 | "6Zi06JKC", 141 | "6Zi06YGT", 142 | "5Lic5Lqs54Ot", 143 | "5p2x5Lqs54ax", 144 | "5q+b5rO95Lic", 145 | "55aG542o", 146 | "5Lmg6L+R5bmz", 147 | "6YKT5bCP5bmz", 148 | "5rGf5rO95rCR", 149 | "6IOh6ZSm5rab", 150 | "5Lmg6L+b5bmz", 151 | "5b2t5Li95aqb", 152 | "6YSn5bCP5bmz", 153 | "5YWa5ZCO6JCO", 154 | "5aSp5a6J6Zeo5bGg5p2A", 155 | "6KKr5Lit5YWx", 156 | "5YWx54uX", 157 | "5Lic5YyX54us56uL", 158 | "5YWx5Lqn5YWa", 159 | "5YWa5Lit5aSu", 160 | "6JeP54us", 161 | "5Lmx5aW4", 162 | "5Lmx5Lym57G7", 163 | "5Lmx5Lym5bCP", 164 | "5LqC5YCr", 165 | "5o+S5bGB5bGB", 166 | "5aeQ5YyF5aSc", 167 | "6bih5be0", 168 | "5YW86IGM5LiK6Zeo", 169 | "6aqa5aaH", 170 | "6aqa56m0", 171 | "6K+x5aW4", 172 | "5o2i5aa7", 173 | "5rex5ZaJ", 174 | "5ZC56JCn", 175 | "6L2u5aW4", 176 | "5bCP56m0", 177 | "6bKN6bG8", 178 | "5aSr5aa75Lqk5o2i", 179 | "6Zmw5ZSH", 180 | "6Zmw6YGT", 181 | "5ZCD57K+", 182 | "5ZCe57K+", 183 | "5YaF5bCE", 184 | "54ix5ray", 185 | "5rC15Y67", 186 | "5rC15Y676L2m5LuR5bel5Yqb", 187 | "5rOVKuWKnw==", 188 | "5rOVbHVu5Yqf", 189 | "5pON5LuW", 190 | "5pON5L2g", 191 | "5pON5L2g5aaI", 192 | "5pON6JuL", 193 | "5pel5L2g5aaI", 194 | "5pel5q275L2g", 195 | "5Y675L2g5aaI55qE", 196 | "5YK76YC8", 197 | "6Im5", 198 | "6I2J5rOl6ams", 199 | "5L2g5aaI55qE", 200 | "5bmy5L2g5aiY", 201 | "5oiR5pON5L2g", 202 | "6Z2g5L2g5aaI", 203 | "5p2C56eN", 204 | "5pel6LWa", 205 | "5Yqe6K+B", 206 | "5b2p56Wo", 207 | "5YKs55yg5rC0", 208 | "5YKs5oOF57KJ", 209 | "5YKs5oOF6I2v", 210 | "5YKs5oOF6Jel", 211 | "5Y+R56Wo5Ye6", 212 | "5Y+R56Wo5Luj", 213 | "5Y+R56Wo6ZSA", 214 | "55m856Wo", 215 | "6L+35aW46I2v", 216 | "6L+35oOF5rC0", 217 | "6L+35oOF6I2v", 218 | "6L+36Jel", 219 | "5Luj5Yqe", 220 | ]; 221 | //关键字及回复列表 222 | var autoReply = []; 223 | // 自动回复关键字判断 224 | var returnHtmlReply = { 225 | htmlReply: "", 226 | htmlReply2: null, 227 | state: false, 228 | dfa: {}, 229 | }; 230 | //关键字排除 231 | var outsideWord = ["微信公众号『小帽集团』", "资源仓库", "@Xiao_MaoMao_bot"]; 232 | // api key 233 | var commandWord = [ 234 | { api: "/tq", apiId: 0 }, 235 | { api: "/suo", apiId: 1 }, 236 | { api: "/music", apiId: 2 }, 237 | { api: "/phone", apiId: 3 }, 238 | { api: "/tg", apiId: 4 }, 239 | { api: "/djt", apiId: 5 }, 240 | { api: "/video", apiId: 6 }, 241 | { api: "/yy", apiId: 7 }, 242 | { api: "/hi", apiId: 8 }, 243 | { api: "/chat", apiId: 9 }, 244 | { api: "/myid", apiId: 10 }, 245 | { api: "/start", apiId: 11 }, 246 | { api: "/help", apiId: 11 }, 247 | { api: "/lan", apiId: 12 }, 248 | { api: "/sao", apiId: 13 }, 249 | { api: "/reply", apiId: 14 }, 250 | { api: "/ban", apiId: 15 }, 251 | { api: "/unban", apiId: 16 }, 252 | { api: "/restrict", apiId: 17 }, 253 | { api: "/hot", apiId: 18 }, 254 | { api: "/db", apiId: 19 }, 255 | { api: "/xz", apiId: 20 }, 256 | { api: "/hl", apiId: 21 }, 257 | ]; 258 | 259 | // 通用类键盘 -------------------------------- 260 | // 定义底部自定义键盘 261 | var followKeyboard = [ 262 | [{ text: "懒人配置" }, { text: "免费节点" }, { text: "订阅转换" }], 263 | [{ text: "图文教程" }, { text: "脚本合集" }, { text: "广告拦截" }], 264 | [{ text: "接口查询" }, { text: "资源仓库" }, { text: "电报解禁" }], 265 | ]; 266 | // 定义在线内联键盘 267 | var followMessageKeyboard = [ 268 | [ 269 | { text: "QX仓库", url: "https://github.com/xiaomaoJT/QxScript" }, 270 | { text: "Bot仓库", url: "https://github.com/xiaomaoJT/TgBot" }, 271 | ], 272 | [ 273 | { text: "✚ 频道", url: "https://t.me/xiaomaoJT" }, 274 | { text: "✚ 群聊", url: "https://t.me/hSuMjrQppKE5MWU9" }, 275 | { text: "✚ 脚本", url: "https://t.me/XiaoMaoScript" }, 276 | ], 277 | [{ text: "✚ 微信公众号『小帽集团』 ✚", callback_data: "WXGROUP" }], 278 | ]; 279 | // 定义底部键盘 280 | var keyboardParams = { 281 | keyboard: followKeyboard, 282 | resize_keyboard: true, //自动调整比例 283 | one_time_keyboard: true, // 是否一次性 284 | is_persistent: true, // 是否一直存在 285 | selective: true, // 是否对特定用户展示 286 | }; 287 | // 定义在线回复消息键盘选项 288 | var keyboardFollowParams = { 289 | inline_keyboard: followMessageKeyboard, 290 | }; 291 | 292 | // 特别类 293 | var resourceWarehouseKeyboardFollowParams = { 294 | inline_keyboard: [ 295 | [ 296 | { text: "QX仓库", url: "https://github.com/xiaomaoJT/QxScript" }, 297 | { text: "Bot仓库", url: "https://github.com/xiaomaoJT/TgBot" }, 298 | ], 299 | [ 300 | { text: "Surge仓库", url: "https://github.com/xiaomaoJT/Surge" }, 301 | { text: "Loon仓库", url: "https://github.com/xiaomaoJT/Loon" }, 302 | { text: "Stash仓库", url: "https://github.com/xiaomaoJT/stash" }, 303 | { text: "Clash仓库", url: "https://github.com/xiaomaoJT/clash" }, 304 | ], 305 | [ 306 | { text: "✚ 频道", url: "https://t.me/xiaomaoJT" }, 307 | { text: "✚ 群聊", url: "https://t.me/hSuMjrQppKE5MWU9" }, 308 | { text: "✚ 脚本", url: "https://t.me/XiaoMaoScript" }, 309 | ], 310 | [{ text: "✚ 微信公众号『小帽集团』 ✚", callback_data: "WXGROUP" }], 311 | ], 312 | }; 313 | 314 | // 群管类参数 -------------------------------- 315 | let followMessageManageKeyboard = [ 316 | [ 317 | { text: "✚ 频道", url: "https://t.me/xiaomaoJT" }, 318 | { text: "✚ 群聊", url: "https://t.me/hSuMjrQppKE5MWU9" }, 319 | { text: "✚ 脚本", url: "https://t.me/XiaoMaoScript" }, 320 | ], 321 | [{ text: "✚ 微信公众号『小帽集团』 ✚", callback_data: "WXGROUP" }], 322 | ]; 323 | 324 | let keyboardFollowManageParams = { 325 | inline_keyboard: followMessageManageKeyboard, 326 | }; 327 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/Modules/Utils.gs: -------------------------------------------------------------------------------- 1 | /** 2 | * 工具类 3 | * 4 | * 无需改动 5 | */ 6 | 7 | /** 8 | * 用于截取api关键字后查询内容 9 | * @param key 10 | * @param keyApi 11 | * @returns 12 | */ 13 | const getString = (key, keyApi) => { 14 | const apiString = key.split(keyApi)[1] || ""; 15 | return apiString.replace(/\s*/g, "").replace("@Xiao_MaoMao_bot", ""); 16 | }; 17 | 18 | /** 19 | * 用于api接口参数识别 20 | * @param commandList 21 | * @param key 22 | * @returns 23 | */ 24 | const isApi = (commandList, key) => { 25 | let isApiStatus = { 26 | status: false, 27 | id: null, 28 | api: "", 29 | }; 30 | commandList.forEach((command) => { 31 | if (key.indexOf(command.api) != -1) { 32 | isApiStatus.status = true; 33 | isApiStatus.id = command.apiId; 34 | isApiStatus.api = command.api; 35 | } 36 | }); 37 | return isApiStatus; 38 | }; 39 | 40 | /** 41 | * 格式化日期对象 42 | * @returns 43 | */ 44 | const getNowDate = () => { 45 | let date = new Date(); 46 | let sign2 = ":"; 47 | let year = date.getFullYear(); // 年 48 | let month = date.getMonth() + 1; // 月 49 | let day = date.getDate(); // 日 50 | let hour = date.getHours(); // 时 51 | let minutes = date.getMinutes(); // 分 52 | let seconds = date.getSeconds(); //秒 53 | let weekArr = ["周日", "周一", "周二", "周三", "周四", "周五", "周六"]; 54 | let week = weekArr[date.getDay()]; 55 | // 给一位数的数据前面加 “0” 56 | if (month >= 1 && month <= 9) { 57 | month = "0" + month; 58 | } 59 | if (day >= 0 && day <= 9) { 60 | day = "0" + day; 61 | } 62 | if (hour >= 0 && hour <= 9) { 63 | hour = "0" + hour; 64 | } 65 | if (minutes >= 0 && minutes <= 9) { 66 | minutes = "0" + minutes; 67 | } 68 | if (seconds >= 0 && seconds <= 9) { 69 | seconds = "0" + seconds; 70 | } 71 | return ( 72 | year + 73 | "/" + 74 | month + 75 | "/" + 76 | day + 77 | " " + 78 | hour + 79 | sign2 + 80 | minutes + 81 | sign2 + 82 | seconds 83 | ); 84 | }; 85 | 86 | /** 87 | * 获取unix时间戳 88 | * @param t N分钟后 Nm ; N天后 Nd 89 | * @returns 90 | */ 91 | const getUnixTime = (t = "") => { 92 | let text = t.toLowerCase().replace(/\s*/g, ""); 93 | if (text.indexOf("d") != -1) { 94 | let dealText = text.replace("d", "") * -1; 95 | return getGoneDay(dealText); 96 | } else if (text.indexOf("m") != -1) { 97 | let dealText = text.replace("m", "") * 1; 98 | return getGoneMinutes(dealText); 99 | } else { 100 | return 0; 101 | } 102 | 103 | // 获取N分钟后的时间 104 | function getGoneMinutes(params = 0) { 105 | let date = new Date(); 106 | let min = date.getMinutes() + 1; 107 | date.setMinutes(min + params); 108 | let y = date.getFullYear(); 109 | let m = 110 | date.getMonth() + 1 < 10 111 | ? "0" + (date.getMonth() + 1) 112 | : date.getMonth() + 1; 113 | let d = date.getDate() < 10 ? "0" + date.getDate() : date.getDate(); 114 | let h = date.getHours() < 10 ? "0" + date.getHours() : date.getHours(); 115 | let f = 116 | date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes(); 117 | let s = 118 | date.getSeconds() < 10 ? "0" + date.getseconds() : date.getSeconds(); 119 | let formatDate = y + "-" + m + "-" + d + " " + h + ":" + f + ":" + s; 120 | return Math.floor(new Date(formatDate).getTime() / 1000); 121 | } 122 | 123 | // 获取N天后的时间 124 | function getGoneDay(n = 0, yearFlag = true) { 125 | let myDate = new Date(); 126 | myDate.setDate(myDate.getDate() - n); 127 | let month = myDate.getMonth() + 1; 128 | let day = myDate.getDate(); 129 | let result = 130 | "" + 131 | (yearFlag ? myDate.getFullYear() : "") + 132 | "/" + 133 | (month < 10 ? "0" + month : month) + 134 | "/" + 135 | (day < 10 ? "0" + day : day); 136 | return Math.floor(new Date(result).getTime() / 1000); 137 | } 138 | }; 139 | 140 | /** 141 | * 142 | * 敏感词过滤算法 143 | * 敏感词已放置于代码前置 144 | * 因gas性能有限,暂只收录124条常用敏感词 145 | */ 146 | const checkSensitiveDFA = (content) => { 147 | // 特殊符号过滤逻辑 148 | let ignoreChars = 149 | " \t\r\n~!@#$%^&*()_+-=【】、{}|;':\",。、《》?αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ。,、;:?!…—·ˉ¨‘’“”々~‖∶"'`|〃〔〕〈〉《》「」『』.〖〗【】()[]{}ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ⒈⒉⒊⒋⒌⒍⒎⒏⒐⒑⒒⒓⒔⒕⒖⒗⒘⒙⒚⒛㈠㈡㈢㈣㈤㈥㈦㈧㈨㈩①②③④⑤⑥⑦⑧⑨⑩⑴⑵⑶⑷⑸⑹⑺⑻⑼⑽⑾⑿⒀⒁⒂⒃⒄⒅⒆⒇≈≡≠=≤≥<>≮≯∷±+-×÷/∫∮∝∞∧∨∑∏∪∩∈∵∴⊥∥∠⌒⊙≌∽√§№☆★○●◎◇◆□℃‰€■△▲※→←↑↓〓¤°#&@\︿_ ̄―♂♀┌┍┎┐┑┒┓─┄┈├┝┞┟┠┡┢┣│┆┊┬┭┮┯┰┱┲┳┼┽┾┿╀╁╂╃└┕┖┗┘┙┚┛━┅┉┤┥┦┧┨┩┪┫┃┇┋┴┵┶┷┸┹┺┻╋╊╉╈╇╆╅╄"; 150 | let ignoreObj = {}; 151 | for (let i = 0, j = ignoreChars.length; i < j; i++) { 152 | ignoreObj[ignoreChars.charCodeAt(i)] = true; 153 | } 154 | 155 | //有限机构建方法 156 | function buildMap(wordList) { 157 | const result = {}; 158 | for (let i = 0, len = wordList.length; i < len; ++i) { 159 | let map = result; 160 | const word = wordList[i]; 161 | for (let j = 0; j < word.length; ++j) { 162 | const ch = word.charAt(j).toLowerCase(); 163 | if (map[ch]) { 164 | map = map[ch]; 165 | if (map.empty) { 166 | break; 167 | } 168 | } else { 169 | if (map.empty) { 170 | delete map.empty; 171 | } 172 | map[ch] = { 173 | empty: true, 174 | }; 175 | map = map[ch]; 176 | } 177 | } 178 | } 179 | return result; 180 | } 181 | 182 | //获取敏感词并解密 183 | function getSensitiveWords() { 184 | // GAS 解密方法 185 | let words = 186 | sensitiveEncodeList.map((word) => 187 | Utilities.newBlob(Utilities.base64Decode(word)).getDataAsString() 188 | ) || []; 189 | 190 | return words; 191 | } 192 | 193 | const sensitiveWords = getSensitiveWords() || []; 194 | let map = buildMap(sensitiveWords) || {}; 195 | 196 | //检测机制 197 | function check(content) { 198 | const result = []; 199 | let stack = []; 200 | let point = map; 201 | for (let i = 0, len = content.length; i < len; ++i) { 202 | const code = content.charCodeAt(i); //转Unicode 203 | if (ignoreObj[code]) { 204 | continue; 205 | } 206 | const ch = content.charAt(i); 207 | const item = point[ch.toLowerCase()]; //转小写 208 | if (!item) { 209 | i = i - stack.length; 210 | stack = []; 211 | point = map; 212 | } else if (item.empty) { 213 | stack.push(ch); 214 | result.push(stack.join("")); 215 | stack = []; 216 | point = map; 217 | } else { 218 | stack.push(ch); 219 | point = item; 220 | } 221 | } 222 | return result; 223 | } 224 | 225 | let sensitiveCheckWords = { 226 | words: [], 227 | wordLength: 0, 228 | }; 229 | sensitiveCheckWords.words = check(content); 230 | sensitiveCheckWords.wordLength = sensitiveCheckWords.words.length; 231 | 232 | return sensitiveCheckWords; 233 | }; 234 | 235 | /** 236 | * 判断时间是否是今天 237 | * @param givenTimeString 238 | * @returns 239 | */ 240 | function isSameDay(givenTimeString) { 241 | // 将字符串转换为日期对象 242 | const givenTime = new Date(givenTimeString); 243 | // 获取当前日期 244 | const currentTime = new Date(); 245 | // 比较年份、月份和日期 246 | return ( 247 | givenTime.getFullYear() === currentTime.getFullYear() && 248 | givenTime.getMonth() === currentTime.getMonth() && 249 | givenTime.getDate() === currentTime.getDate() 250 | ); 251 | } 252 | 253 | /** 254 | * 对象根据total排序 255 | * @param obj 256 | * @returns 257 | */ 258 | function sortByTotalDescending(obj) { 259 | return Object.entries(obj) 260 | .sort(([, a], [, b]) => b.total - a.total) 261 | .map(([key, value]) => value); 262 | } 263 | 264 | 265 | /** 266 | * 判断消息类型 267 | * @param MESSAGE 原始消息体 268 | * @param typeParams 消息类型 269 | * @returns 270 | */ 271 | function getMessageType(MESSAGE, typeParams) { 272 | return MESSAGE[typeParams].hasOwnProperty("text") 273 | ? "[文本消息]" 274 | : MESSAGE[typeParams].hasOwnProperty("sticker") 275 | ? "[表情消息]" 276 | : MESSAGE[typeParams].hasOwnProperty("photo") 277 | ? "[图片消息]" 278 | : MESSAGE[typeParams].hasOwnProperty("video") 279 | ? "[视频消息]" 280 | : MESSAGE[typeParams].hasOwnProperty("document") 281 | ? "[文件消息]" 282 | : MESSAGE[typeParams].hasOwnProperty("voice") 283 | ? "[音频消息]" 284 | : "[未知消息类型]"; 285 | } 286 | -------------------------------------------------------------------------------- /Apps Script/MaoBot@OfficialVersion/README.md: -------------------------------------------------------------------------------- 1 | #### 🎟 XiaoMaoBot 正式版源码解析 2 | 3 | 4 | 5 | ##### 目录代码结构 6 | 7 | - MaoBot@OfficialVersion -- 正式版目录 8 | - DB -- 表格数据库 9 | - XiaoMaoBot_DB.xlsx -- 数据库初始化表格 10 | - Modules -- 模块化代码 11 | - MaoBot.gs -- 主体响应脚本 12 | - Params.gs -- 参数配置脚本 13 | - Core.gs -- 核心逻辑脚本 14 | - Manage.gs -- 群管逻辑脚本 15 | - Api.gs -- 接口逻辑脚本 16 | - Utils.gs -- 工具类脚本 17 | 18 | 19 | 20 | ##### 🌠 DB数据库 21 | 22 | ```text 23 | 初始化数据表 24 | 1、新建Google表格 25 | 2、选择「文件」-「导入」-「上传本地文件XiaoMaoBot_DB.xlsx」-「替换电子表格」-「导入数据」 26 | 27 | 数据表结构介绍 28 | · db_telegram -- 主体储存表 29 | 「无需操作,自动写入」 30 | 31 | · key_params -- 关键字配置表 32 | 「自动回复关键字内容,按表头要求填写,支持无限多个,支持多段文本持续回复」 33 | 「⚠️数据缓存默认3小时刷新一次」 34 | 35 | · authority_management -- 权限控制表 36 | 「群组消息屏蔽、管理员权限释放」 37 | ``` 38 | 39 | 40 | 41 | ##### 🎇 Modules模块代码 42 | 43 | ###### 部署时请按目录结构顺序放置模块代码!!「MaoBot.gs」务必放置于第一位!!! 44 | 45 | ###### 仅标注「 ✅ 」代码需要填写 46 | 47 | 48 | 49 | **「MaoBot.gs」** 50 | 51 | ```text 52 | 主体响应脚本 53 | 54 | doPost -- TG消息接收函数 55 | processData -- 消息处理函数 56 | processReplyWord -- 关键字处理函数 57 | pushDataToKing -- 消息推送函数 58 | ``` 59 | 60 | 61 | 62 | **「Params.gs」✅** 63 | 64 | ```javascript 65 | // 参数配置脚本 66 | 67 | // 务必完善 EXECID 和 BOTID 68 | var EXECID = ""; // Google EXEC ID - 谷歌表格ID 69 | var BOTID = ""; // Telegram BOT ID key - tg机器人Token 70 | 71 | // 建议补全 KingId 和 botIdAlone 72 | var KingId = ""; // 取主人tg id - 私人消息主动功能必须填写此项 73 | var botIdAlone = ""; // 取 bot id 用于识别引用消息 74 | 75 | // 其余参数无需改动,参数声明请见脚本内注释 76 | ``` 77 | 78 | 79 | 80 | **「Core.gs」** 81 | 82 | ```text 83 | 核心逻辑脚本 84 | 85 | linkBot -- TG连通函数 86 | readSpreadsheet -- 表格数据读取函数 87 | convertString -- 关键字内容处理函数 88 | getKeyWords -- 关键字列表构建函数 89 | getCacheData -- 关键字缓存函数 90 | getCacheAuthorityList -- 权限列表缓存函数 91 | setStorage -- 消息存储函数 92 | ``` 93 | 94 | 95 | 96 | **「Manage.gs」** 97 | 98 | ```text 99 | 群管逻辑脚本 100 | 101 | getUnBanUser -- 解禁用户函数 102 | deleteUserMessage -- 删除信息函数 103 | getBanUser -- 封禁用户函数 104 | getRestrictUser -- 禁言用户函数 105 | getReply -- 主动回复函数 106 | ``` 107 | 108 | 109 | 110 | **「Api.gs」** 111 | 112 | ```text 113 | 接口逻辑脚本 114 | 115 | apiReply -- 接口前置函数 116 | getSao -- 骚话大全 117 | getLanLink -- 蓝奏云解析 118 | getChatBot -- ChatGPT 119 | getHelloBot -- 聊天机器人 120 | getVideo -- 随机视频 121 | getDuJiTang -- 毒鸡汤 122 | getTianGou -- 舔狗日记 123 | getYiYan -- 一言 124 | getPhoneWhere -- 号码归属地 125 | getMusic -- 随机音乐 126 | getLinkShort -- 短链接生成 127 | getWeatherApi -- 天气查询 128 | getHotList -- 热榜查询 129 | getHoroscopeList -- 星座运势 130 | getDouBan -- 豆瓣电影查询 131 | ``` 132 | 133 | 134 | 135 | 136 | 137 | **「Utils.gs」** 138 | 139 | ```text 140 | 工具类脚本 141 | 142 | getString -- api内容处理函数 143 | isApi -- api响应逻辑函数 144 | getNowDate -- 日期格式化函数 145 | getUnixTime -- Unix时间戳函数 146 | checkSensitiveDFA -- 敏感词过滤函数 147 | ``` 148 | 149 | -------------------------------------------------------------------------------- /Apps Script/README.md: -------------------------------------------------------------------------------- 1 | **[TgBot](https://github.com/xiaomaoJT/TgBot)** ***https://github.com/xiaomaoJT/TgBot*** **@XiaoMao** 2 | 3 | **[<< 回到首页](https://github.com/xiaomaoJT/TgBot)** 4 | 5 | 6 | 7 | ------------ 8 | 9 | MaoBot@OfficialVersion -- 正式版 10 | 11 | MaoBot@BetaVersion -- 测试版 12 | 13 | -------------------------------------------------------------------------------- /Apps Script/配置图解/Google Apps Script 脚本部署1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/Google Apps Script 脚本部署1.png -------------------------------------------------------------------------------- /Apps Script/配置图解/Google Apps Script 脚本部署2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/Google Apps Script 脚本部署2.png -------------------------------------------------------------------------------- /Apps Script/配置图解/Google表格加载脚本位置 - Google Apps Script.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/Google表格加载脚本位置 - Google Apps Script.png -------------------------------------------------------------------------------- /Apps Script/配置图解/README.md: -------------------------------------------------------------------------------- 1 | ##### 🎟 配置图解 -------------------------------------------------------------------------------- /Apps Script/配置图解/bot消息主动回复功能演示.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/bot消息主动回复功能演示.png -------------------------------------------------------------------------------- /Apps Script/配置图解/数据表结构 - Google表格.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/数据表结构 - Google表格.png -------------------------------------------------------------------------------- /Apps Script/配置图解/运行效果及数据存储.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaomaoJT/TgBot/a564d31a417dbb5bfd1aaadd9e53c1697356d113/Apps Script/配置图解/运行效果及数据存储.png -------------------------------------------------------------------------------- /COURSE.md: -------------------------------------------------------------------------------- 1 | **[TgBot](https://github.com/xiaomaoJT/TgBot)** ***https://github.com/xiaomaoJT/TgBot*** **@XiaoMao** 2 | 3 | **[<< 回到首页](https://github.com/xiaomaoJT/TgBot)** 4 | 5 | 6 | 7 | ```js 8 | /** 9 | * author : @XiaoMao 10 | * # 小版本更新请查看更新日志 | 或加入xiaomao组织⬇️ 11 | * # 微信公众号 【小帽集团】 12 | * # XiaoMao · Tg频道频道:https://t.me/xiaomaoJT 13 | * 14 | * 测试版 - 已停止维护 - 建议移步正式版 15 | * 测试版 - 已停止维护 - 建议移步正式版 16 | * 测试版 - 已停止维护 - 建议移步正式版 17 | * 测试版 - 已停止维护 - 建议移步正式版 18 | * 测试版 - 已停止维护 - 建议移步正式版 19 | * 20 | * Google App Script 21 | * 用于执行tg机器人功能 22 | * 功能描述:❶ 超级群管功能❷ 广告词/敏感词过滤、自动删除/警告❸ 多样化接口查询、XiaoMao数据加工❹ 自定义聊天窗快捷键盘/消息跟随按钮❺ 关键字消息/私聊消息 自动回复❻ 私聊消息/群组消息 捕捉及消息私人推送❼ 私聊消息/群组消息 自动存储 23 | * 24 | * 功能细则:入群检测、退群检测、入群欢迎、退群欢送、超级群管功能、用户封禁、用户解封、用户禁言、广告词敏感词拦截及自动删除、chatGPT查询、消息私人推送、BOT消息主动回复、自动接口查询及数据加工、自定义键盘、私聊及自动回复、关键字自动回复、消息存储等功能 25 | * 26 | * 源码开发不易,使用引用请注明出处! 27 | * 源码开发不易,使用引用请注明出处! 28 | * 源码开发不易,使用引用请注明出处! 29 | */ 30 | ``` 31 | 32 | 33 | 34 | ------------ 35 | 36 | #### 🎟 Telegram Bot 快速使用教程 37 | 38 | > 1. 关注并激活[XiaoMaoBot机器人](https://t.me/Xiao_MaoMao_bot)。 39 | > 2. 将XiaoMaoBot机器人拉入群聊。 40 | > 3. 授予XiaoMaoBot机器人管理员权限。 41 | > 4. 至此即可自动开启XiaoMaoBot机器人所有功能。 42 | 43 | ------ 44 | 45 | ------ 46 | 47 | 48 | 49 | #### 🎟 Telegram Bot 搭建教程 50 | 51 | > ##### ⏺️ 视频教程地址:https://www.alipan.com/s/z21rrmknNYZ 52 | > 53 | > *- 视频录制特别鸣谢大佬**@BFJWR*** 54 | 55 | > 轻松上手,带你免费打造属于自己的telegram机器人 56 | > 57 | > [三分钟免费注册外区Apple ID 教程](https://mp.weixin.qq.com/s/YzYsF9QyHZVJK9P7bsrURQ) 58 | 59 | ###### 🚗 第一步:创建\登陆谷歌账号,并打开[谷歌云端硬盘](https://drive.google.com/drive/my-drive?ths=true) 60 | 61 | ```text 62 | 1⃣️ Google账号请自行创建,此处不作详细说明~ 63 | 2⃣️ 登陆Google账号,并打开Google Drive 64 | 3⃣️ 谷歌云端硬盘地址:https://drive.google.com/drive/my-drive?ths=true 65 | ``` 66 | 67 | ###### 🚗 第二步:新建 Google 表格 68 | 69 | ```text 70 | 1⃣️ 左上角新建,选择Google 表格即可。 71 | 2⃣️ 浏览器地址栏,记下表格ID 72 | ``` 73 | 74 | ```javascript 75 | //例如 76 | https://docs.google.com/spreadsheets/d/XXXXXXX这一串就是表格IDXXXXXXXXXXX/edit#gid=0 77 | ``` 78 | 79 | ```text 80 | 3⃣️ 修改表格名称及表格底部工作表名(右键,重命名) 81 | 4⃣️ 至此,表格新建完毕。 82 | ``` 83 | 84 | ```text 85 | ⚠️ 注意事项: 86 | 1⃣️ 表格ID很重要,需记下来❕ 87 | 2⃣️ 工作表名很重要,需记下来❕ 88 | 3⃣️ 注意工作表名和表格名的区别,工作表名是表格底下的名称,不是顶上的❕❕❕ 89 | ``` 90 | 91 | ###### 🚗 第三步:创建个人tg机器人 92 | 93 | ```text 94 | 1⃣️ telegram搜索 @BotFather 机器人 95 | 2⃣️ 点击 @BotFather 菜单 /newbot 或 自行输入 96 | 3⃣️ 待机器人响应后 按要求输入 自己的机器人名称,注意名称要求小写字母,且以bot结尾,例如maobot或者mao_bot 等等 97 | 4⃣️ 如果提示被使用,则继续重新输入新名称,直到可用为止 98 | 5⃣️ 创建成功后,将自动生成一串token代码,即为你的机器人ID码 99 | 6⃣️ 通过 @BotFather 菜单 /setjoingroups ,选择机器人后,点击Enable按钮打开机器人加群功能,开启后可通过机器人详情页,实现新增到群组或频道功能。 100 | 7⃣️ 至此机器人创建完成。 101 | ``` 102 | 103 | ```text 104 | ⚠️ 注意事项: 105 | 1⃣️ 机器人token很重要,需记下来❕ 106 | 2⃣️ 如果泄漏,可以通过@BotFather进行重置。 107 | 3⃣️ 机器人可通过 @BotFather 发送 /mybots 指令进行类似描述、头像、关于、指令等基本设定,这里不做深究 108 | 109 | 更多教程可查看:https://ithelp.ithome.com.tw/articles/10245264 110 | ``` 111 | 112 | ###### 🚗 第四步:创建Google Apps Script函数,完成机器人部署 113 | 114 | > [MaoBot.gs 代码](https://raw.githubusercontent.com/xiaomaoJT/TgBot/main/Apps%20Script/MaoBot.gs/maoBot.gs) 115 | > 116 | > [部署图解](https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script/配置图解) 117 | 118 | ```text 119 | 1⃣️ 打开刚刚创建的Google表格,点击工具栏 扩展程序 > Apps脚本 120 | 2⃣️ 复制maoBot.gs代码,⚠️并全部覆盖粘贴到脚本内【⚠️请注意删除网页上自带的所有代码!】 121 | 3⃣️ 完善EXECID - 谷歌表格ID;在引号内填入自己的谷歌表格ID 122 | 4⃣️ 完善EXECNAME - 谷歌表格 工作表名;在引号内填入自己的谷歌表格工作表名 123 | 5⃣️ 完善BOTID - tg机器人Token;在引号内填入自己的tg机器人Token 124 | 6⃣️ ⚠️保存代码,文件名可随意自定义【⚠️必须手动保存一次,注意顶部myfunction函数变为doPost函数,未切换请手动切换doPost】 125 | 7⃣️ 点击部署 > 新建部署 126 | 8⃣️ 选择部署类型 > web应用 127 | 9⃣️ 描述自行随意填写,有访问权限的人员 选择 任何人 128 | 🔟点击部署,进行全部授权,全部允许(左下角小字点开,选择展开后最下面的按钮) 129 | 1⃣️1⃣️ 部署成功后,复制web应用网址,替换下面链接,并复制到浏览器打开进行机器人激活 130 | 1⃣️2⃣️ 完成,可以通过群聊或者私聊自己的机器人进行消息回复了,并且交互的信息将会自动存于Google表格中。 131 | 1⃣️3⃣️ 群聊请注意授予机器人管理权限,部分功能激活请根据源码参数解析提示补充完整必要参数。 132 | ``` 133 | 134 | ```javascript 135 | https://api.telegram.org/bot 你的tg机器人Token /setWebhook?url=你的web应用网址 136 | //上面链接替换 你的tg机器人Token和你的web应用网址 内容即可 137 | //⚠️注意链接不要留空格 138 | //⚠️注意链接不要留空格 -- 空格会被浏览器自动转义成 %20 ,请注意排查问题~ 139 | //⚠️注意链接不要留空格 140 | //注意token前面有bot需要保留 141 | 142 | //替换完成后,复制链接到浏览器打开,返回下方内容皆为成功 143 | {"ok":true,"result":true,"description":"Webhook is set"} 144 | {"ok":true,"result":true,"description":"Webhook is already set"} 145 | 146 | //返回下面内容,则tg机器人Token失效,请通过 @BotFather 进行重置,然后再次执行第四步 重新部署 147 | {"ok":false,"error_code":401,"description":"Unauthorized"} 148 | 149 | 源码开发不易,使用引用请注明出处!遇到问题欢迎留言~ 150 | 151 | ⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️⚠️ 152 | 153 | 请注意,以上激活状态仅代表gas WEB部署完成,并不代表可完整运行。 154 | 155 | 完整功能激活请注意检查以下几点: 156 | 1、必要参数填写完整 ⚠️ 157 | 2、私聊检测【私聊机器人 所有能力表现完整✅】 158 | 3、群聊内赋予机器人管理权限。 159 | 4、google表格内容会随群聊或机器人私聊自动捕捉信息并实时写入⚠️【私聊表格必须有新数据写人✅】 160 | 161 | 🚨代码错误排查 162 | 1、打开Google Apps Script 163 | 2、左侧菜单打开 脚本执行 164 | 3、查看允许日志及报错提示 165 | 4、根据提示再问题追踪 166 | ``` 167 | 168 | ------ 169 | 170 | 171 | 172 | ##### 🚗 新功能 · 内容补充 173 | 174 | > **新特性补充-1.1** 175 | > 176 | > 此补充内容**自更新日期[20230220] 版本号@Beta4.3-228 起**。 177 | > 178 | > 补充内容为新升级项,非必填,若不填写则此服务不生效。 179 | > 180 | > 新增私人消息推送服务,涉及源码顶部参数 **KingId 、 KingType 、KingInfo** 181 | > 182 | > KingID可通过私聊 [XiaoMao机器人](https://t.me/Xiao_MaoMao_bot) 回复 **/myid** 获取 183 | 184 | ```javascript 185 | 私人消息推送服务,即会将机器人采集的指定内容实时推送给服务所有者或指定人。 186 | 当前消息详情支持 表情、图片、视频、文件、音频 五种类型消息,其他类型消息只推送消息内容。 187 | 188 | // 取需要推送的主人 TG 的 chat_id 189 | var KingId = ""; 190 | // 1 全部类型 191 | // 2 群聊 + 私聊类型 192 | // 3 私聊类型 193 | // 4 群聊类型 194 | var KingType = 1; 195 | // 1 推送详情(原图片、视频、贴纸等) 196 | // 0 仅推送基础消息 197 | var KingInfo = 1; 198 | 199 | 200 | 推送内容预览: 201 | 202 | 🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄🍄 203 | 204 | XiaoMaoBot捕捉到用户讯息 205 | 206 | 📝 简要内容:[文本消息] XXXX 207 | 🎎 原始用户:XXXX 208 | 🏖 来源位置:来自 [私聊] 209 | 🛎 发送时间:2023/06/29 14:05:06 210 | 📰 原始数据: 211 | {"update_id":xxxx,"message":{"message_id":xxxx,"from":{"id":xxxx,"is_bot":false,"first_name":"xxxx","username":"xxxx","language_code":"zh-hans"},"chat":{"id":xxxx,"first_name":"xxxx","username":"xxxx","type":"private"},"date":xxxx,"voice":{"duration":0,"mime_type":"audio/ogg","file_id":"xxxx","file_unique_id":"xxxx","file_size":4200}}} 212 | 213 | 🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀🍀 214 | ``` 215 | ------ 216 | >**新特性补充-1.2** 217 | > 218 | >**Bot消息主动回复 - 私人消息推送功能升级补充项** 更新日期[20230621] 版本 **@Beta4.4-417** 219 | > 220 | >[**部署图解**](https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script/配置图解) 221 | > 222 | >以下特性仅适用于开启私人消息推送服务后的MaoBot。 223 | > 224 | >------ 225 | > 226 | >**使用限制:** 227 | > 228 | >1、主动回复功能仅适用于Bot主人,即KingId所属者。 229 | > 230 | >2、主动回复功能仅可通过部署后的MaoBot私聊窗口进行回复。 231 | > 232 | >3、版本@Beta4.4-417 - 当前版本仅支持主动回复文本消息。 233 | > 234 | >4、不建议频繁使用Bot主动私聊功能,避免造成误封等不必要的麻烦,后果自负。 235 | > 236 | >------ 237 | > 238 | >**使用教程:** 239 | > 240 | >通过部署后的私人MaoBot聊天框,引用MaoBot所推送的【私聊】或【群聊】消息,通过私有指令 **/reply + 主动回复内容** 来激活主动回复功能。 241 | > 242 | >**私有指令:** 243 | > 244 | >/reply + 主动回复内容 245 | > 246 | >------ 247 | > 248 | >**使用效果:** 249 | > 250 | >1、正确引用消息并发送指令后,将收到反馈【**✅ 私聊信息已发送成功**】 251 | > 252 | >2、错误引用消息,将收到失败反馈。 253 | > 254 | >3、主动回复可支持回复 私聊消息 及 群聊消息,群聊消息支持针对性引用式回复。 255 | > 256 | >------ 257 | > 258 | >------ 259 | > 260 | >**新特性补充-1.3** 261 | > 262 | >群管功能:封禁用户、解封用户 ;更新日期[20230628] 版本 **@Beta4.4-434** 263 | > 264 | >**使用方法:** 265 | > 266 | >与主动回复功能类似,但仅支持【群聊】消息,当前版本为永久封禁。 267 | > 268 | >**私有指令:** 269 | > 270 | >【封禁用户】/ban 271 | > 272 | >- 【自更新日期[20230629] 版本 **@Beta4.4-444**起,封禁功能支持自定义封禁时间】 273 | > 274 | >- 封禁时长分为三种 275 | > 276 | >- 1、N分钟:Nm 如30分钟:30m 例:/ban 30m 277 | > 278 | >- 2、N天:Nd 如30天:30d 例:/ban 30d 279 | > 280 | >- 3、不填:永久封禁 例:/ban 281 | > 282 | >- 【封禁用户】/ban + 时长 283 | > 284 | > 285 | > 286 | >【解封用户】/unban 287 | > 288 | >------ 289 | > 290 | >------ 291 | > 292 | >**新特性补充-1.4** 293 | > 294 | >群管功能:禁言用户 ;更新日期[20230629] 版本 **@Beta4.4-444** 295 | > 296 | >**使用方法:** 297 | > 298 | >与主动回复功能类似,但仅支持【群聊】消息,封禁时长规则请见**新特性补充-1.3**。 299 | > 300 | >**私有指令:** 301 | > 302 | >【禁言用户】/restrict + 时长 303 | > 304 | 305 | ------ 306 | 307 | 308 | 309 | > **新特性补充-2** 310 | > 311 | > 此补充内容**自更新日期[20230322] 版本号@Beta4.3-291 起**。 312 | > 313 | > *新增参数**botIdAlone**,用于识别引用类型消息的回复* 314 | > 315 | > botIdAlone 取机器人的id 316 | 317 | ------ 318 | 319 | 320 | 321 | > **新特性补充-3** 322 | > 323 | > 此补充内容**自更新日期[20230505] 版本号@Beta 4.4-382 起**。 324 | > 325 | > *新增参数**forGotList**,用于过滤需要排除捕捉的群组信息* 326 | > 327 | > forGotList 取*请填入群组id,多个用,间隔 如 ['22222','11111]* 328 | 329 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **[TgBot@XiaoMao](https://github.com/xiaomaoJT/TgBot)** 2 | ***https://github.com/xiaomaoJT/TgBot*** 3 | 4 | 5 | 6 | ------------ 7 | 8 | #### Telegram Bot机器人,基于Google Apps Script实现。 9 | 10 | | **最近更新时间** | **2025年05月26日** | 11 | | :--------------- | :----------------------------------------------------------- | 12 | | **当前版本** | **正式版 - V1.41** | 13 | | **功能清单** | **功能描述:** ❶ 超级群管功能❷ 广告词/敏感词过滤、自动删除/警告❸ 多样化接口查询、XiaoMao数据加工❹ 自定义聊天窗快捷键盘/消息跟随按钮❺ 关键字消息/私聊消息 自动回复❻ 私聊消息/群组消息 捕捉及消息私人推送❼ 私聊消息/群组消息 自动存储
**功能细则:** 入群检测、退群检测、入群欢迎、退群欢送、超级群管功能、用户封禁、用户解封、用户禁言、广告词敏感词拦截及自动删除、chatGPT查询、消息私人推送、BOT消息主动回复、自动接口查询及数据加工、自定义键盘、私聊及自动回复、关键字自动回复、消息存储、消息自动删除等功能 | 14 | | **作者** |
**⚠️ 源码开发不易,使用引用请注明出处!⚠️**

**@XiaoMao

⚠️ 源码开发不易,使用引用请注明出处!⚠️**

| 15 | | **成本** | **完全免费|持续更新** | 16 | | **机器人** | [**XiaoMaoBot机器人 在线快速体验**](https://t.me/Xiao_MaoMao_bot) | 17 | | **安全检测** | **已通过OSCS社区的安全工具检测,该项目暂无安全风险** | 18 | 19 | 20 | 21 | ------------ 22 | 23 | ##### 🎟 XiaoMao频道 · 群组 24 | 25 |
26 | 27 | github 28 | 29 | 30 | github 31 | 32 | 33 | github 34 | 35 | 36 | github 37 | 38 |
39 | 40 | 41 | 42 | ------ 43 | 44 | ##### 🎟 快速导航 · 目录 45 | 46 | - ▶️ [正式版部署 - 视频教程](https://www.alipan.com/s/dW2yPirBysi) ✅ 47 | 48 | - 🚀 [正式版部署 - 文字教程](https://github.com/xiaomaoJT/TgBot/blob/main/Apps%20Script/MaoBot%40OfficialVersion/COURSE.md) ✅ 49 | - 🛰️ [数据表部署 - 填写教程 ✅](https://github.com/xiaomaoJT/TgBot/blob/main/Apps%20Script/MaoBot%40OfficialVersion/DB/COURSE.md) 50 | - 🚁 [正式版源码解析](https://github.com/xiaomaoJT/TgBot/blob/main/Apps%20Script/MaoBot%40OfficialVersion/README.md) 51 | 52 | 53 | 54 | - 📖 [仓库资源更新日志](https://github.com/xiaomaoJT/TgBot/blob/main/UPDATELOG.md) **小版本模块更新说明请参见更新日志** 55 | 56 | - 🚗 [测试版部署 - 文字教程](https://github.com/xiaomaoJT/TgBot/blob/main/COURSE.md) 57 | 58 | 59 | 60 | ------------ 61 | 62 | 63 | ##### 🎟 目录构成 64 | + ###### [Apps Script](https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script) **总目录** 65 | 66 | + **MaoBot@OfficialVersion** -- 小帽机器人正式版「**模块化部署(持续更新)**」 67 | + **DB** -- 初始化数据表 68 | + **Modules** -- 模块化代码 69 | 70 | + **MaoBot@BetaVersion** -- 小帽机器人测试版「**一体化部署(停止维护)**」 71 | 72 | + **配置图解** 73 | + **BotTest** 74 | + **BaseBot** -- 基础用法 75 | + **HighBot** -- 经典案例 76 | 77 | 78 | 79 | ⚠️ 请注意,自正式版「V1.00」起,Beta测试版本将停止维护并不再更新,后续更新将于正式版「**MaoBot@OfficialVersion**」中进行。 80 | 81 | 82 | 83 | ------ 84 | 85 | #### 📋 To-Do / 待办列表 86 | 87 | **1、Graphic message [ Completion of development 🎉 ] 「20240823 - V1.20+」** 88 | 89 | - 图文消息 [开发完成 🎉] 「完成于20240823,版本V1.20+已支持」 90 | 91 | **2、Channel information monitor, query, keyword push [Not yet started]** 92 | 93 | - 频道消息监听、查询、关键词推送 [未开始...] 94 | 95 | **3、Message auto-destruction [ Completion of development 🎉 ] 「20250312 - V1.30+」 [⚠️ Due to GAS limitations, high concurrency is only supported at 20 times per 30 seconds]** 96 | 97 | - 消息自动删除 [开发完成 🎉] 「完成于20250312,版本V1.30+已支持」「受限于GAS,高并发仅支持20次/30s」 98 | 99 | **4、Hot deployment [Not yet started]** 100 | 101 | - 热更新 [未开始...] 102 | 103 | 104 | 105 | ------ 106 | 107 | ##### 🎟 Github Stats 108 | 109 |
110 | 111 |
112 | 113 | 114 | ------ 115 | 116 | 117 | ##### 🎟 Visitor Counter 118 | 119 |
120 | 121 |
122 | 123 | 124 | 125 | ------------ 126 | 127 | #### 🎟 ***声明*** 128 | 129 | - 此项目中仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 130 | - 由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 131 | - 请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 132 | - 此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 133 | - 本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 134 | - 如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我将在收到认证文件确认后删除此脚本。 135 | - 所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 136 | -------------------------------------------------------------------------------- /UPDATELOG.md: -------------------------------------------------------------------------------- 1 | **[TgBot](https://github.com/xiaomaoJT/TgBot)** ***https://github.com/xiaomaoJT/TgBot*** **@XiaoMao** 2 | 3 | **[<< 回到首页](https://github.com/xiaomaoJT/TgBot)** 4 | 5 | 6 | 7 | ------ 8 | 9 | ##### 🎟 XiaoMao频道 · 群组 10 | 11 |
12 | 13 | github 14 | 15 | 16 | github 17 | 18 | 19 | github 20 | 21 | 22 | github 23 | 24 |
25 | 26 | 27 | 28 | ------------ 29 | 30 | ##### 🎟 更新日志 31 | 32 | > 如何更新?在Google表格的Apps脚本中,替换maobot代码,保存并部署即可。 33 | 34 | > 如何更新部署?右上角部署 > 管理部署 > 编辑 > 选择新版本,输入版本描述 > 部署即可生效。 35 | 36 | > 旧部署将自动归档;请注意保留前三行 EXECID 、 EXECNAME 、 BOTID 参数,及新参数 KingId、botIdAlone。 37 | 38 | > 诸如行内键盘调用类更新将不作细致更新记录,最新更新时间以首页或本页或版本号为主。 39 | 40 | > 本仓自2022年9月10日起 已持续更新 ***167*** 次 41 | 42 | > ***最新更新时间 2025.05.26 14:15*** 43 | 44 | 45 | + **20250526** 46 | 47 | * > 更新MaoBotV1.41「更新模块 『 Manage.gs 』」 48 | 49 | * > 修复群管理授权失败。 50 | 51 | 52 | + **20250508** 53 | 54 | * > 更新MaoBotV1.33「更新模块 『 Manage.gs 』」 55 | 56 | * > 新增群管理自动获取,群管功能将自动授权于群管理。 57 | 58 | + **20250408** 59 | 60 | * > 更新MaoBotV1.32「更新模块 『 maoBot.gs 』」 61 | 62 | * > 修复 因触发器失败而导致的消息删除问题 63 | 64 | 65 | + **20250326** 66 | 67 | * > 更新MaoBotV1.31「更新模块 『 maoBot.gs 』」 68 | 69 | * > 修复 机器人消息定时自动删除 失效问题 70 | 71 | 72 | + **20250312** 73 | 74 | * > 更新MaoBotV1.30「更新模块 『 TgBot/Apps Script/MaoBot@OfficialVersion/Modules 』」 75 | 76 | * > 新增 机器人消息自动删除 77 | 78 | 79 | + **20240823** 80 | 81 | * > 更新MaoBotV1.20「更新模块 『 maoBot.gs 』、『 Core.gs 』、『XiaoMaoBot_DB.xlsx』」 82 | 83 | * > 新增 图文消息 、 视频消息 回复 84 | 85 | * > 请注意,本次更新针对初始化表『XiaoMaoBot_DB.xlsx』进行升级,关键字表『key_params』新增标识字段,⚠️旧版本已不再支持,需更新!⚠️ 86 | 87 | 88 | + **20240815** 89 | 90 | * > 更新MaoBotV1.11「更新模块 『 maoBot.gs 』、『 Core.gs 』、『XiaoMaoBot_DB.xlsx』」 91 | 92 | * > 新增 MarkdownV2 格式回复文本 93 | 94 | 95 | + **20240812** 96 | 97 | * > 更新MaoBotV1.08「更新模块 『 Params.gs 』、『 Api.gs 』、『 Utils.gs 』」 98 | 99 | * > 新增话痨排行榜功能 100 | 101 | + **20240807** 102 | 103 | * > 优化管理员权限释放逻辑 104 | 105 | * > 更新初始化表格式 106 | 107 | + **20240805** 108 | 109 | * > 正式版V1.00发布 110 | 111 | + **20240726** 112 | 113 | * > 新增视频教程 - 视频录制特别鸣谢大佬@BFJWR 114 | 115 | + **20240513** 116 | 117 | * > 更新maobot,内部测试版本号@Beta4.6-634。 118 | 119 | * > 补全部分提示。 120 | 121 | + **20240321** 122 | 123 | * > 更新maobot,内部测试版本号@Beta4.6-628。 124 | 125 | * > 更新关键词绝杀序位关键字banKeyLastIndex。 126 | 127 | 128 | + **20240129** 129 | 130 | * > 更新maobot,内部测试版本号@Beta4.6-619。 131 | 132 | * > 更新关键词绝杀函数,默认取敏感词库前7位为绝杀关键字。 133 | 134 | * > 更新部分教程文案。 135 | 136 | 137 | + **20240123** 138 | 139 | * > 更新maobot,内部测试版本号@Beta4.6-615。 140 | 141 | * > 执行/ban /retrict 管理员指令时,将自动删除群聊内违规信息,并执行封禁操作。 142 | 143 | 144 | + **20240119** 145 | 146 | * > 更新maobot,内部测试版本号@Beta4.6-605。 147 | 148 | * > 剔除过渡态replyWord2参数,更改为replyWordMore数组格式,关键字多回复可按数组方式放入字段的replyWordMore中,详见/js_vip 指令。 149 | 150 | + **20240111** 151 | 152 | * > 更新maobot,内部测试版本号@Beta4.5-594。 153 | 154 | * > 新增doPost入口函数报错优化(新手向)。 155 | 156 | 157 | + **20231228** 158 | 159 | * > 更新maobot,内部测试版本号@Beta4.5-592。 160 | 161 | * > 优化代码逻辑及函数结构。 162 | 163 | + **20231222** 164 | 165 | * > 更新maobot,内部测试版本号@Beta4.5-587。 166 | 167 | * > 新增强杀广告功能,触发关键字将直接ban人「被ban需管理手动解禁,否则永封」。 168 | 169 | * > 优化部分算法细节及部分取值变化。 170 | 171 | 172 | + **20231215** 173 | 174 | * > 更新maobot,内部测试版本号@Beta4.5-577。 175 | 176 | * > 开放管理功能于群聊管理员「封禁、解封、禁言」BETA - 请完善新参数【PermissionReleaseList】。 177 | 178 | + **20231214** 179 | 180 | * > 更新maobot,内部测试版本号@Beta4.5-569。 181 | 182 | * > 修复管理员权限操作时「封禁、解封、禁言」,因被控用户未关注机器人而导致的响应失败。 183 | 184 | + **20231213** 185 | 186 | * > 更新maobot,内部测试版本号@Beta4.5-564。 187 | 188 | * > 初步解决消息体字符超长问题,启用replyWord2参数「待自适应完善,当前为过渡态」。 189 | 190 | + **20231017** 191 | 192 | * > 更新maobot,内部测试版本号@Beta4.5-504。 193 | 194 | * > 完善部分教程及注意事项。 195 | 196 | + **20230918** 197 | 198 | * > 更新maobot,内部测试版本号@Beta4.4-489。 199 | 200 | * > 优化底部键盘激发后的聊天框内容引用BUG。 201 | 202 | + **20230804** 203 | 204 | * > 更新maobot,内部测试版本号@Beta4.4-471。 205 | 206 | * > 修复私人推送中来源为群聊类型时undefined用户跳转。 207 | 208 | + **20230803** 209 | 210 | * > 更新maobot,内部测试版本号@Beta4.4-470。 211 | 212 | * > 优化@机器人时响应优先级。 213 | 214 | 215 | + **20230717** 216 | 217 | * > 更新maobot,内部测试版本号@Beta4.4-462。 218 | 219 | * > 修复部分错误,优化因预设回复过长而导致的无响应问题。 220 | 221 | 222 | + **20230706** 223 | 224 | * > 更新maobot,内部测试版本号@Beta4.4-459。 225 | 226 | * > 修复部分错误指令下的语法逻辑。 227 | 228 | + **20230705** 229 | 230 | * > 更新maobot,内部测试版本号@Beta4.4-456。 231 | 232 | * > 新增api新功能 /hot /db /xz。 233 | 234 | + **20230629** 235 | 236 | * > 更新maobot,内部测试版本号@Beta4.4-444。 237 | 238 | * > 新增功能群管功能:禁言用户。 239 | 240 | * > 优化部分关键字的响应逻辑。 241 | 242 | 243 | + **20230628** 244 | 245 | * > 更新maobot,内部测试版本号@Beta4.4-435。 246 | 247 | * > 新增功能群管功能:封禁用户、解封用户。 248 | 249 | * > 主人ID私聊信息取消推送。 250 | 251 | 252 | + **20230625** 253 | 254 | * > 更新maobot,内部测试版本号@Beta4.4-420。 255 | 256 | * > 修复部分api,补充新参数文档。 257 | 258 | 259 | 260 | + **20230621** 261 | 262 | * > 更新maobot,内部测试版本号@Beta4.4-417。 263 | 264 | * > 新增功能【bot消息主动回复:即使用bot身份回复他人消息】,当前版本仅适用于回复文本消息。 265 | 266 | 267 | + **20230505** 268 | 269 | * > 更新maobot,内部测试版本号@Beta4.4-382。 270 | 271 | * > 新增参数【forGotList】。 272 | 273 | + **20230418** 274 | 275 | * > 更新maobot,内部测试版本号@Beta4.4-377。 276 | 277 | * > 修复新逻辑下非文本消息捕捉失效问题。 278 | 279 | * > 新增捕捉内容中私聊类型的用户定位。 280 | 281 | + **20230409** 282 | 283 | * > 更新maobot,内部测试版本号@Beta4.4-362。 284 | 285 | * > 升级api请求。 286 | 287 | 288 | + **20230407【跨版本更新·⚠️·需要更新·修复重大bug】** 289 | 290 | * > 更新maobot,内部测试版本号@Beta4.4-354。 291 | 292 | * > 修复重大逻辑Bug。 293 | 294 | * > 加快响应速度,理论上加快6倍。 295 | 296 | 297 | + **20230323** 298 | 299 | * > 补充源码注释及部分教程。 300 | 301 | 302 | + **20230322** 303 | 304 | * > 更新maobot,内部测试版本号@Beta4.3-291。 305 | 306 | * > 新增参数botIdAlone,用于识别引用类型消息的回复 307 | 308 | * > 提升智能回复,针对引用消息进行捕捉回复 309 | 310 | 311 | + **20230320** 312 | 313 | * > 更新maobot,内部测试版本号@Beta4.3-285。 314 | 315 | * > 优化响应策略,新增对提及消息的支持 316 | 317 | 318 | + **20230317** 319 | 320 | * > 更新maobot,内部测试版本号@Beta4.3-273。 321 | 322 | * > 优化响应策略 323 | 324 | * > 优化@类型消息错误捕捉 325 | 326 | 327 | + **20230303** 328 | 329 | * > 更新maobot,内部测试版本号@Beta4.3-266。 330 | 331 | * > 优化start消息回复 332 | 333 | * > 优化api查询时查询数据为空的情况 334 | 335 | 336 | + **20230224** 337 | 338 | * > 更新maobot,内部测试版本号@Beta4.3-264。 339 | 340 | * > 优化私人消息推送内容,新增非私聊讯息内容来源跳转,优化推送排版 341 | 342 | * > 优化消息存储时非文本消息的undefined报错 343 | 344 | + **20230222** 345 | 346 | * > 更新maobot,内部测试版本号@Beta4.3-253。 347 | 348 | * > 新增对表情、图片、视频、文件、音频消息的识别 349 | 350 | * > 新增隐私指令【 /myid 】,可用于获取自身 tg_chat_id,请注意私聊机器人调用。 351 | 352 | * > 优化私人消息推送,新增详情推送服务及[教程](https://github.com/xiaomaoJT/TgBot/blob/main/COURSE.md) 353 | 354 | * > 新增可用于测试的样例[maoBotTest.gs](https://raw.githubusercontent.com/xiaomaoJT/TgBot/main/Apps%20Script/MaoBot.gs/maoBotTest.gs) 355 | 356 | * > 新增表格内容消息类型存储 357 | 358 | 359 | + **20230221** 360 | 361 | * > 更新maobot,内部测试版本号@Beta4.3-249。 362 | 363 | * > 优化私人消息推送机制 ,新增推送量级选择 364 | 365 | * > 新增对私聊消息中非文本类型消息的识别及响应 366 | 367 | * > 优化提及类型消息的识别及响应 368 | 369 | 370 | + **20230220** 371 | 372 | * > 更新maobot,内部测试版本号@Beta4.3-228。 373 | 374 | * > 新增chatGPT二次聚合接口(原接口为减少调用次数,将优先调用数据库数据,即重复问题的回答可能一致,长时间未回复请尝试更换问题,最长等待时间10min,并已接入违禁词拦截,请勿询问政治敏感、色情低俗、赌、毒等违法内容,消息经二次加工而得,可能存在滞后,请注意文明玩耍,勿恶意刷屏) 375 | 376 | * > 新增机器人消息私人推送,目前为全量推送(测试中,当前类型可能造成频繁推送),请至源码顶处完善KingId,即主人的chat_id 377 | 378 | * > 优化关键字响应,减少无关回复 379 | 380 | * > 优化示例 381 | 382 | + **20230117** 383 | 384 | * > 更新maobot,内部测试版本号@Beta4.3-200。 385 | 386 | * > 更新键盘栏响应。 387 | 388 | + **20230114** 389 | 390 | * > 更新maobot,内部测试版本号@Beta4.2198。 391 | 392 | * > 更新底部键盘栏配置。 393 | 394 | + **20230110** 395 | 396 | * > 更新maobot,内部测试版本号@Beta4.2192。 397 | 398 | * > 剔除响应时间,新增响应延迟计算方法(/delay方法期限开放)。 399 | 400 | * > 优化响应逻辑,提高响应速度。 401 | 402 | + **20230109** 403 | 404 | * > 更新maobot,内部测试版本号@Beta4.2182。 405 | 406 | * > 新增响应关键字及解决入口。 407 | 408 | * > 优化响应逻辑,提高响应速度。 409 | 410 | + **20230106** 411 | 412 | * > 更新maobot,内部测试版本号@Beta4.2180。 413 | 414 | * > 新增QX教程按钮。 415 | 416 | * > 优化响应逻辑,提高响应速度。 417 | 418 | + **20221228** 419 | 420 | * > 更新maobot,内部测试版本号@Beta4.2179。 421 | 422 | * > 下线疫情查询接口功能。 423 | 424 | * > 优化响应逻辑,提高响应速度。 425 | 426 | + **20221215** 427 | 428 | * > 更新maobot,内部测试版本号@Beta4.2178。 429 | 430 | * > 上线入群欢迎、退群欢送功能。 431 | 432 | * > 优化响应逻辑,提高响应速度。 433 | 434 | + ##### 20221128(灰度测试中) 435 | 436 | * > 更新maobot,内部测试版本号@Beta4.2175。 437 | 438 | * > 新增入群欢迎、退群欢送功能(功能测试中)。 439 | 440 | * > 优化响应逻辑,提高响应速度。 441 | 442 | * > 新增Bot.gs模块脚本。 443 | 444 | + ##### 20221126 445 | 446 | * > 更新maobot,内部测试版本号@Beta4.11。 447 | 448 | * > 修复随机小姐姐视频接口。 449 | 450 | + ##### 20221124 451 | 452 | * > 更新maobot,内部测试版本号@Beta4.10。 453 | 454 | * > 修复部分bug。 455 | 456 | + ##### 20221117 457 | 458 | * > 更新maobot,内部测试版本号@Beta4.08。 459 | 460 | * > 修复所有api接口,简化接口调用语句。 461 | 462 | * > 升级api调用方式,采用json格式,数据由XiaoMao加工而得。 463 | 464 | + ##### 20221116 465 | 466 | * > 更新maobot,内部测试版本号@Beta4.02。 467 | 468 | * > 修复部分api接口,更改接口策略,由XiaoMao加工而得。 469 | 470 | + ##### 20221114 471 | 472 | * > 更新maobot,内部测试版本号@Beta3.99。 473 | 474 | * > 优化自定义键盘配置,新增tg解限制 按钮。 475 | 476 | + ##### 20221113 477 | 478 | * > 更新maobot,内部测试版本号@Beta3.97。 479 | 480 | * > 新增基于DFA算法的聊天消息敏感词过滤(checkSensitiveDFA方法)。 481 | 482 | * > 优化部分mention类型消息错误提示。 483 | 484 | * > 修复部分BUG。 485 | 486 | + ##### 20221109 487 | 488 | * > 更新maobot,内部测试版本号@Beta3.84。 489 | 490 | * > 增加api指令错误捕捉。 491 | 492 | * > 新增去广告指令。 493 | 494 | * > 修复部分BUG。 495 | 496 | + ##### 20221028 497 | 498 | * > 更新maobot,内部测试版本号@Beta3.80。 499 | 500 | * > 优化masBot部分api指令,防止误判。 501 | 502 | * > 新增启用机器人时start指令的识别。 503 | 504 | + ##### 20221026 505 | 506 | * > 更新maobot,内部测试版本号@Beta3.78。 507 | 508 | * > 优化部分资源失效问题。 509 | 510 | * > 优化部分字段undefined存储。 511 | 512 | + ##### 20221021 513 | 514 | * > 更新maobot,内部测试版本号@Beta3.76。 515 | 516 | * > 优化讯息存储结构,增加回复语句存储(数据存储更改,需同步修改Google表格结构),[配置图解 点击查看](https://github.com/xiaomaoJT/TgBot/tree/main/Apps%20Script/配置图解)。 517 | 518 | ```text 519 | 发起时间 用户ID 用户名称 用户昵称 消息类型 消息来源 来源ID 消息内容 消息JSON 520 | ``` 521 | 522 | * > 修复消息回调键盘失效的问题。 523 | 524 | * > 新增接口聊天机器人 ,通过指令 /hi 激活 ,例如/hi 早上好 525 | 526 | * > 新增国内疫情查询 , 通过指令 /yq 激活 , 例如/yq 广东 527 | 528 | + ##### 20221017 529 | 530 | * > 更新maobot源码参数详解。 531 | 532 | + ##### 20221016 533 | 534 | * > 免费节点订阅地址开放 [XiaoMao-Forever](https://gist.githubusercontent.com/xiaomaoJT/921025f761277153bebb30abde7f784f/raw/XiaoMao-Forever)。 535 | 536 | * > 内部测试版本号 MaoBot @Beta3.2 537 | 538 | + ##### 20221014 539 | 540 | * > 更新maoBot.gs存储优化,避免id串自动格式化。 541 | 542 | * > 优化自动回复逻辑,仅对 私聊消息、关键字、@消息 三种类型进行回复。 543 | 544 | * > 内部测试版本号 MaoBot @Beta3.1 545 | 546 | + ##### 20221013 547 | 548 | * > 更新maoBot.gs回复内容设定及语句换行样式优化。 549 | 550 | * > 内部测试版本号 MaoBot @Beta2.3 551 | 552 | + ##### 20221009 553 | 554 | * > MaoBot @Beta2.0 555 | 556 | * > 新增maoBot参数描述 557 | 558 | * > 升级功能,增加接口查询:天气状况查询、短链接生成、抖音热搜榜、手机号码查询、网站测速、酷狗音乐、腾讯视频、中国农历查询 559 | 560 | + ##### 20221008 561 | 562 | * > MaoBot @Beta1.1 563 | 564 | * > 增加关键字回复功能,优化自动回复配置 565 | 566 | * > 增加使用教程 567 | 568 | + ##### 20221001 569 | 570 | * > MaoBot @Beta1.0 第一测试版 571 | 572 | * > 可实现自定义键盘、消息跟随键盘、自动回复、消息存储等功能 573 | 574 | + ##### 20220930 575 | 576 | * > bot基础代码 577 | 578 | 579 | 580 | ------ 581 | 582 | ##### 🎟 Visitor Counter 583 | 584 |
585 | 586 |
587 | 588 | 589 | 590 | 591 | ------------ 592 | 593 | #### 🎟 ***声明*** 594 | 595 | - 此项目中仅用于学习研究,不保证其合法性、准确性、有效性,请根据情况自行判断,本人对此不承担任何保证责任。 596 | - 由于此脚本仅用于学习研究,您必须在下载后 24 小时内将所有内容从您的计算机或手机或任何存储设备中完全删除,若违反规定引起任何事件本人对此均不负责。 597 | - 请勿将此脚本用于任何商业或非法目的,若违反规定请自行对此负责。 598 | - 此脚本涉及应用与本人无关,本人对因此引起的任何隐私泄漏或其他后果不承担任何责任。 599 | - 本人对任何脚本引发的问题概不负责,包括但不限于由脚本错误引起的任何损失和损害。 600 | - 如果任何单位或个人认为此脚本可能涉嫌侵犯其权利,应及时通知并提供身份证明,所有权证明,我将在收到认证文件确认后删除此脚本。 601 | - 所有直接或间接使用、查看此脚本的人均应该仔细阅读此声明。本人保留随时更改或补充此声明的权利。一旦您使用或复制了此脚本,即视为您已接受此免责声明。 602 | 603 | --------------------------------------------------------------------------------