├── .gitignore
├── LICENSE
├── README.md
├── bot
├── analytics.js
├── floodProtect.js
├── init.js
├── parseSauceNao.js
└── request.js
├── package-lock.json
├── package.json
├── settings
├── private.js.example
└── settings.js
└── tools
└── tools.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 |
6 | # Runtime data
7 | pids
8 | *.pid
9 | *.seed
10 |
11 | # Directory for instrumented libs generated by jscoverage/JSCover
12 | lib-cov
13 |
14 | # Coverage directory used by tools like istanbul
15 | coverage
16 |
17 | # nyc test coverage
18 | .nyc_output
19 |
20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21 | .grunt
22 |
23 | # node-waf configuration
24 | .lock-wscript
25 |
26 | # Compiled binary addons (http://nodejs.org/api/addons.html)
27 | build/Release
28 |
29 | # Dependency directories
30 | node_modules
31 | jspm_packages
32 |
33 | # Optional npm cache directory
34 | .npm
35 |
36 | # Optional REPL history
37 | .node_repl_history
38 |
39 | # private
40 | settings/private.js
41 |
42 | # trash
43 | **/*.js~
44 |
45 | # note
46 | note.txt
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [@reverseSearchBot](https://t.me/reverseSearchBot)
2 |
3 | Finds the source of an image/GIF/sticker/video or identify the anime character, show, episode & time from its screenshot. Also works in groups if you reply to an image with commands "/source" and "/sauce". Use the inline mode for URL lookups and sharing the sauce with buttons.
4 |
5 | Scrapes from ~~Tineye~~ (fuck) and Saucenow. Doesn't use the standard API.
6 |
7 | A more user friendly and functional rewrite of https://github.com/cod119/TeleSaucenao_bot
8 |
9 | ### Usage:
10 |
11 | Rename settings/private.js.example to .js after putting the required keys/tokens and constants.
12 |
13 | `npm i`
14 |
15 | `npm start`
16 |
--------------------------------------------------------------------------------
/bot/analytics.js:
--------------------------------------------------------------------------------
1 | import { privateSettings, urls } from "../settings/settings.js";
2 |
3 | export const track = async (msgFrom, eventType, eventProps) => {
4 | if (!msgFrom) return;
5 | const lang = msgFrom.language_code || "undefined";
6 |
7 | const data = {
8 | api_key: privateSettings.analKey,
9 | events: [
10 | {
11 | platform: privateSettings.botName,
12 | ip: "$remote",
13 | event_type: eventType,
14 | event_properties: eventProps,
15 | user_id: msgFrom.id,
16 | language: lang,
17 | },
18 | ],
19 | };
20 |
21 | try {
22 | await fetch(urls.analUrl, {
23 | headers: {
24 | Accept: "*/*",
25 | "Content-Type": "application/json",
26 | },
27 | method: "POST",
28 | body: JSON.stringify(data),
29 | });
30 | } catch (e) {
31 | console.log("anal failed " + e);
32 | }
33 | };
34 |
--------------------------------------------------------------------------------
/bot/floodProtect.js:
--------------------------------------------------------------------------------
1 | import { floodProtect } from "../settings/settings.js";
2 | import { LRUCache } from "lru-cache";
3 | const userLRU = new LRUCache({ max: 100 });
4 |
5 | export const isFlooding = (ctx) => {
6 | if (!ctx.message || !ctx.from || !ctx.message.date) return false;
7 | let userId = ctx.from.id;
8 | let user = userLRU.get(userId);
9 | let now = ctx.message.date;
10 | let diffSecs;
11 | if (!user) {
12 | diffSecs = 0;
13 | user = { lastTime: now, numMsgs: 1, warned: false };
14 | userLRU.set(userId, user);
15 | } else {
16 | diffSecs = now - user.lastTime;
17 |
18 | user.lastTime = now;
19 | user.numMsgs++;
20 | }
21 | if (diffSecs < floodProtect.interval) {
22 | if (user.numMsgs > floodProtect.msgLimit) {
23 | if (!user.warned) {
24 | ctx.reply(floodProtect.message);
25 | user.warned = true;
26 | }
27 | console.log(userId + " is flooding");
28 | return true; //flooding
29 | } else return false;
30 | } else {
31 | user.numMsgs = 2;
32 | // user.warned = false;
33 | return false;
34 | }
35 | };
36 |
--------------------------------------------------------------------------------
/bot/init.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | global.debug = false;
4 |
5 | import {
6 | privateSettings,
7 | msgs,
8 | id_buttonName,
9 | keywords,
10 | commands,
11 | } from "../settings/settings.js";
12 |
13 | import { Telegraf, Markup } from "telegraf";
14 | import { errInFetch, fetchSauceNao } from "./request.js";
15 | import { parseSauceNao } from "./parseSauceNao.js";
16 | import { isSupportedExt, editMessageText } from "../tools/tools.js";
17 | import { track } from "./analytics.js";
18 | import { isFlooding } from "./floodProtect.js";
19 | import { message } from "telegraf/filters";
20 |
21 | const bot = new Telegraf(privateSettings.botToken);
22 |
23 | process.on("unhandledRejection", (e) => {
24 | console.log("unhandledRejection:");
25 | console.dir(e);
26 | });
27 |
28 | const loadingKb = Markup.inlineKeyboard([
29 | Markup.button.callback(id_buttonName.loading, "noop"),
30 | ]);
31 |
32 | bot.start((ctx) => ctx.reply(msgs.help, { parse_mode: "HTML" }));
33 |
34 | //added to group
35 | bot.on(message("new_chat_members"), (ctx) => {
36 | const token = privateSettings.botToken;
37 | const myId = token.substring(0, token.indexOf(":"));
38 | if (ctx.message.new_chat_participant.id == myId)
39 | ctx.reply(msgs.help, { parse_mode: "HTML" });
40 | });
41 | const keywordResponse = (ctx) => {
42 | if (isFlooding(ctx)) return;
43 | const rmsg = ctx.message.reply_to_message;
44 |
45 | if (rmsg && (rmsg.photo || rmsg.document || rmsg.sticker || rmsg.video)) {
46 | if (
47 | (rmsg.forward_from && rmsg.forward_from.id == 792028928) ||
48 | rmsg.from.id == 792028928
49 | ) {
50 | ctx.reply("LOL no cheating");
51 | return;
52 | }
53 | getSauceFromFile(rmsg);
54 | track(ctx.from, "sauce_keyword", { text: ctx.message.text });
55 | } else if (ctx.message.text.indexOf("/") == 0) {
56 | ctx.reply(msgs.keywordHelp, {
57 | parse_mode: "HTML",
58 | reply_to_message_id: ctx.message.message_id,
59 | });
60 | track(ctx.from, "sauce_click_only");
61 | }
62 | };
63 |
64 | bot.hears(keywords, keywordResponse);
65 | bot.command(commands, keywordResponse);
66 | bot.command("help", (ctx) => ctx.reply(msgs.help, { parse_mode: "HTML" }));
67 | bot.command("privacy", (ctx) =>
68 | ctx.reply(msgs.privacyPolicy, { parse_mode: "HTML" })
69 | );
70 |
71 | bot.on(
72 | [message("photo"), message("sticker"), message("document"), message("video")],
73 | (ctx) => {
74 | if (ctx.message.forward_from && ctx.message.forward_from.id == 792028928) {
75 | ctx.reply("LOL no cheating");
76 | return;
77 | }
78 |
79 | if (
80 | (ctx.message.chat.type == "private" && !isFlooding(ctx)) ||
81 | (privateSettings.favouriteGroups.indexOf(ctx.message.chat.id) > -1 &&
82 | ctx.message.photo)
83 | )
84 | getSauceFromFile(ctx.message);
85 | }
86 | );
87 |
88 | const getSauceFromFile = (msg) => {
89 | if (msg.photo && msg.photo.length > 0) {
90 | msg.fileId = msg.photo[msg.photo.length - 1].file_id;
91 | track(msg.from, "query", { type: "photo" });
92 | } else if (msg.sticker) {
93 | if (msg.sticker.is_animated) msg.fileId = msg.sticker.thumb.file_id;
94 | else msg.fileId = msg.sticker.file_id;
95 | track(msg.from, "query", { type: "sticker" });
96 | } else if (msg.document) {
97 | if (isSupportedExt(msg.document.file_name)) {
98 | msg.fileId = msg.document.file_id;
99 | track(msg.from, "query", { type: "file" });
100 | } else if (msg.document.mime_type == "video/mp4") {
101 | if (msg.document.thumb) {
102 | msg.fileId = msg.document.thumb.file_id;
103 | track(msg.from, "query", { type: "video" });
104 | } else return;
105 | }
106 | } else if (msg.video && msg.video.thumb) {
107 | msg.fileId = msg.video.thumb.file_id;
108 | track(msg.from, "query", { type: "video" });
109 | } else return;
110 | bot.telegram
111 | .sendMessage(msg.chat.id, msgs.loading, {
112 | parse_mode: "HTML",
113 | reply_to_message_id: msg.message_id,
114 | ...loadingKb,
115 | })
116 | .then((res) => {
117 | getSauce(msg, res);
118 | });
119 | };
120 |
121 | const getSauce = async (msg, editMsg) => {
122 | editMsg.fileId = msg.fileId;
123 | editMsg.url = msg.url;
124 | editMsg.origFrom = msg.from;
125 | if (msg.url) {
126 | await request(msg.url, bot, editMsg);
127 | } else {
128 | try {
129 | const file = await bot.telegram.getFile(msg.fileId);
130 | if (global.debug) console.log("file is", file);
131 |
132 | const url =
133 | "https://api.telegram.org/file/bot" +
134 | privateSettings.botToken +
135 | "/" +
136 | file.file_path;
137 | await request(url, bot, editMsg);
138 | } catch (e) {
139 | console.log("invalidFileId");
140 | await editMessageText(bot, editMsg, msgs.invalidFileId, {
141 | parse_mode: "HTML",
142 | });
143 | }
144 | }
145 | };
146 |
147 | const request = async (url, bot, editMsg) => {
148 | editMsg.url = url;
149 |
150 | try {
151 | const res = await fetchSauceNao(url, editMsg);
152 | const parsedRes = parseSauceNao(res, editMsg);
153 | if (parsedRes) {
154 | const { displayText, markup, preview } = parsedRes;
155 | try {
156 | await editMessageText(bot, editMsg, displayText, {
157 | parse_mode: "HTML",
158 | ...markup,
159 | disable_web_page_preview: !preview,
160 | });
161 | } catch (e) {
162 | errInFetch(e);
163 | }
164 | }
165 | } catch (err) {
166 | let errDisplayText;
167 | if (err.message == msgs.zeroResult) errDisplayText = msgs.zeroResult;
168 | else if (err.message == msgs.reachLimitation) {
169 | errInFetch(err);
170 | errDisplayText = msgs.reachLimitation;
171 | } else {
172 | errInFetch(err);
173 | errDisplayText = msgs.unknownError;
174 | }
175 | await editMessageText(bot, editMsg, errDisplayText, {
176 | parse_mode: "HTML",
177 | disable_web_page_preview: true,
178 | });
179 | }
180 | };
181 |
182 | bot.launch({
183 | dropPendingUpdates: true,
184 | });
185 | // await bot.telegram.deleteWebhook()
186 | // bot.launch({
187 | // webhook: {
188 | // domain: privateSettings.webhookEndpoint,
189 | // hookPath: privateSettings.webhookEndpoint.substring(
190 | // privateSettings.webhookEndpoint.lastIndexOf("/")
191 | // ),
192 | // port: privateSettings.webhookPort,
193 | // host: privateSettings.webhookHost,
194 | // },
195 | // });
196 |
--------------------------------------------------------------------------------
/bot/parseSauceNao.js:
--------------------------------------------------------------------------------
1 | import { Markup } from "telegraf";
2 | import { urls, msgs, sauceNaoParams } from "../settings/settings.js";
3 | import { json2query } from "../tools/tools.js";
4 | import { track } from "./analytics.js";
5 | import { load } from "cheerio";
6 |
7 | export const parseSauceNao = (response, editMsg) => {
8 | const $ = load(response);
9 | let found = false;
10 | const preview = false;
11 | const bList = [];
12 |
13 | const results = [];
14 | const links = {};
15 | const content = { _title: "", _text: "" };
16 | let displayText = "";
17 |
18 | $(".resulttablecontent").each((i, elem) => {
19 | const percent = parseFloat($(elem).find(".resultsimilarityinfo").text());
20 | if (
21 | results.length &&
22 | Math.abs(results[0].percent - percent) > sauceNaoParams.tolerance
23 | )
24 | return;
25 | if (percent < sauceNaoParams.minSimilarity) return;
26 |
27 | found = true;
28 |
29 | $(elem)
30 | .find(".resultcontentcolumn a")
31 | .each((i, elem) => {
32 | let text;
33 | if ($(elem).prev()) text = $(elem).prev().text();
34 | if (!text) return;
35 | text = text.replace("Member", "Artist");
36 |
37 | links[text] = $(elem).attr("href");
38 | if (text == "Artist" && !content._byUsername)
39 | content._byUsername = $(elem).text();
40 | $(elem).prev().remove();
41 | $(elem).remove();
42 | });
43 |
44 | $(elem)
45 | .find(".resulttitle, .resultcontentcolumn")
46 | .each((i, elem) => {
47 | let isCharactersDiv = false;
48 |
49 | let lines = $(elem)
50 | .html()
51 | .replace(/
|
/g, "\n")
52 | .trim();
53 | lines = $(lines).text();
54 |
55 | if (lines) {
56 | lines = lines.split("\n");
57 | for (i in lines) {
58 | if (!lines[i].trim()) continue;
59 | const splits = lines[i].split(": ");
60 | let key = splits[0].trim();
61 | if (splits.length == 2 && splits[1]) {
62 | key = key.replace("Est Time", "Time").replace("Creator", "By");
63 | if (!content[key]) content[key] = splits[1].trim();
64 | } else if (key) {
65 | //a character block has multiple characters
66 | //seperated by \n and nothing else (i think)
67 | if (key == "Characters") {
68 | isCharactersDiv = true;
69 | content.Characters = "";
70 | } else if (isCharactersDiv) {
71 | content.Characters += key;
72 | if (i != lines.length - 1) content.Characters += ", ";
73 | } else if ($(elem).is(".resulttitle") && !content._title)
74 | content._title += lines[i].trim() + "\n";
75 | else content._text += " " + lines[i].trim() + "\n";
76 | }
77 | }
78 | }
79 | });
80 |
81 | $(elem)
82 | .find(".resultmiscinfo > a")
83 | .each((i, elem) => {
84 | let text = $(elem).children().attr("src");
85 | text = text.substring(text.lastIndexOf("/") + 1, text.lastIndexOf("."));
86 | if (links[text]) {
87 | return;
88 | }
89 | links[text] = $(elem).attr("href");
90 | if (text == "anidb") {
91 | // extract episode number
92 | let title;
93 | const matches = content._title.match(/(.+) - ([\d]+)\n$/);
94 | if (matches && matches.length == 3) {
95 | title = matches[1];
96 | content._title = title + " (Ep. " + matches[2] + ")\n";
97 | } else title = content._title;
98 |
99 | links["MAL"] = urls.mal + json2query({ q: title });
100 | }
101 | });
102 |
103 | results.push({ percent: percent });
104 | });
105 |
106 | if (!found) {
107 | throw new Error(msgs.zeroResult);
108 | }
109 | if (content._title) displayText = "" + content._title + "" + "\n";
110 |
111 | delete content._title;
112 |
113 | if (!content.By && content._byUsername) content._byUsername = content.By;
114 |
115 | if (content.Characters) {
116 | displayText += "Character: " + content.Characters + "\n";
117 | delete content.Characters;
118 | }
119 | if (content.Material) {
120 | displayText += "Material: " + content.Material + "\n";
121 | delete content.Material;
122 | }
123 |
124 | for (let key in content) {
125 | if (!(key == "_text" || key == "JPTitle"))
126 | displayText += "" + key + ": " + content[key] + "\n";
127 | }
128 | // put blank line if > 1 line
129 | if ((content._text.match(/\n/g) || []).length > 1)
130 | content._text = "\n" + content._text;
131 |
132 | displayText += content._text;
133 | if (!displayText.trim()) displayText = "-no title-";
134 |
135 | let ctr = 0;
136 | for (const key in links) {
137 | if (ctr >= 6)
138 | //max 6 buttons or 3 lines
139 | break;
140 | let text = key.replace(/:/g, "").replace(/ ID/g, "");
141 | let url = links[key];
142 | if (url.startsWith("//")) url = "http:" + url;
143 | if (ctr == 0) text = "View on " + text;
144 | bList.push(Markup.button.url(text, url));
145 | ctr++;
146 | }
147 |
148 | const markup = Markup.inlineKeyboard(bList, {
149 | wrap: (btn, index, currentRow) => currentRow.length >= 3,
150 | });
151 |
152 | track(editMsg.origFrom, "sauce_found_saucenao");
153 |
154 | return { displayText, markup, preview };
155 | };
156 |
--------------------------------------------------------------------------------
/bot/request.js:
--------------------------------------------------------------------------------
1 | import { socksDispatcher } from "fetch-socks";
2 | import { exec } from "child_process";
3 | import { LRUCache } from "lru-cache";
4 |
5 | import {
6 | privateSettings,
7 | urls,
8 | sauceNaoParams,
9 | msgs,
10 | userAgent,
11 | } from "../settings/settings.js";
12 | import { json2query } from "../tools/tools.js";
13 | import { track } from "./analytics.js";
14 |
15 | const dispatcher = socksDispatcher({
16 | type: 5,
17 | host: privateSettings.socksProxyHost,
18 | port: privateSettings.socksProxyPort,
19 | });
20 |
21 | const cache = new LRUCache({ max: 200, ttl: 1000 * 60 * 60 * 24 });
22 | const myFetch = async (url, editMsg, options) => {
23 | const hit = cache.get(url);
24 |
25 | if (hit && editMsg && editMsg.origFrom) {
26 | track(editMsg.origFrom, "cache_hit");
27 | return hit;
28 | }
29 | options.dispatcher = dispatcher;
30 | options.headers = {
31 | "User-Agent": userAgent,
32 | };
33 |
34 | const res = await fetch(url, options);
35 | if (res.status >= 200 && res.status < 300) {
36 | const txt = res.text();
37 | cache.set(url, txt);
38 |
39 | return txt;
40 | } else {
41 | const error = new Error(res.statusText || res.status);
42 | error.response = res;
43 | if (options.params && options.params.url) error.url = options.params.url;
44 | throw error;
45 | }
46 | };
47 |
48 | export function fetchSauceNao(url, editMsg) {
49 | const params = sauceNaoParams;
50 | params.url = url;
51 | const uurl = urls.sauceNao + json2query(params);
52 |
53 | return myFetch(uurl, editMsg, { params: params });
54 | }
55 |
56 | export function errInFetch(err) {
57 | console.log("errInFetch");
58 |
59 | if (err.response) {
60 | // The request was made, but the server responded with a status code
61 | // that falls out of the range of 2xx
62 | console.log("-----error.status is", err.response.status);
63 | if (
64 | err.response.status &&
65 | (err.response.status == 429 || err.response.status == 403)
66 | ) {
67 | let now = Date.now();
68 | if (
69 | now - proxy.lastReqTime > 100 * 1000 &&
70 | privateSettings.changeProxyCommand
71 | ) {
72 | proxy.lastReqTime = now;
73 | exec(privateSettings.changeProxyCommand, (err, stdout, stderr) => {
74 | if (stdout) console.log(stdout);
75 | else if (stderr) console.log(stderr);
76 | });
77 | }
78 | return new Error(msgs.reachLimitation);
79 | }
80 | }
81 | return err;
82 | }
83 |
--------------------------------------------------------------------------------
/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saucenaobot",
3 | "version": "1.0.0",
4 | "lockfileVersion": 2,
5 | "requires": true,
6 | "packages": {
7 | "": {
8 | "name": "saucenaobot",
9 | "version": "1.0.0",
10 | "license": "ISC",
11 | "dependencies": {
12 | "async-mutex": "^0.5.0",
13 | "cheerio": "^1.0.0-rc.12",
14 | "fetch-socks": "^1.3.0",
15 | "lru-cache": "^10.3.1",
16 | "telegraf": "^4.16.3"
17 | }
18 | },
19 | "node_modules/@telegraf/types": {
20 | "version": "7.1.0",
21 | "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz",
22 | "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw=="
23 | },
24 | "node_modules/abort-controller": {
25 | "version": "3.0.0",
26 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
27 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
28 | "dependencies": {
29 | "event-target-shim": "^5.0.0"
30 | },
31 | "engines": {
32 | "node": ">=6.5"
33 | }
34 | },
35 | "node_modules/async-mutex": {
36 | "version": "0.5.0",
37 | "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
38 | "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
39 | "dependencies": {
40 | "tslib": "^2.4.0"
41 | }
42 | },
43 | "node_modules/boolbase": {
44 | "version": "1.0.0",
45 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
46 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
47 | },
48 | "node_modules/buffer-alloc": {
49 | "version": "1.2.0",
50 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
51 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
52 | "dependencies": {
53 | "buffer-alloc-unsafe": "^1.1.0",
54 | "buffer-fill": "^1.0.0"
55 | }
56 | },
57 | "node_modules/buffer-alloc-unsafe": {
58 | "version": "1.1.0",
59 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
60 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
61 | },
62 | "node_modules/buffer-fill": {
63 | "version": "1.0.0",
64 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
65 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
66 | },
67 | "node_modules/cheerio": {
68 | "version": "1.0.0-rc.12",
69 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
70 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
71 | "dependencies": {
72 | "cheerio-select": "^2.1.0",
73 | "dom-serializer": "^2.0.0",
74 | "domhandler": "^5.0.3",
75 | "domutils": "^3.0.1",
76 | "htmlparser2": "^8.0.1",
77 | "parse5": "^7.0.0",
78 | "parse5-htmlparser2-tree-adapter": "^7.0.0"
79 | },
80 | "engines": {
81 | "node": ">= 6"
82 | },
83 | "funding": {
84 | "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
85 | }
86 | },
87 | "node_modules/cheerio-select": {
88 | "version": "2.1.0",
89 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
90 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
91 | "dependencies": {
92 | "boolbase": "^1.0.0",
93 | "css-select": "^5.1.0",
94 | "css-what": "^6.1.0",
95 | "domelementtype": "^2.3.0",
96 | "domhandler": "^5.0.3",
97 | "domutils": "^3.0.1"
98 | },
99 | "funding": {
100 | "url": "https://github.com/sponsors/fb55"
101 | }
102 | },
103 | "node_modules/css-select": {
104 | "version": "5.1.0",
105 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
106 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
107 | "dependencies": {
108 | "boolbase": "^1.0.0",
109 | "css-what": "^6.1.0",
110 | "domhandler": "^5.0.2",
111 | "domutils": "^3.0.1",
112 | "nth-check": "^2.0.1"
113 | },
114 | "funding": {
115 | "url": "https://github.com/sponsors/fb55"
116 | }
117 | },
118 | "node_modules/css-what": {
119 | "version": "6.1.0",
120 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
121 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
122 | "engines": {
123 | "node": ">= 6"
124 | },
125 | "funding": {
126 | "url": "https://github.com/sponsors/fb55"
127 | }
128 | },
129 | "node_modules/dom-serializer": {
130 | "version": "2.0.0",
131 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
132 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
133 | "dependencies": {
134 | "domelementtype": "^2.3.0",
135 | "domhandler": "^5.0.2",
136 | "entities": "^4.2.0"
137 | },
138 | "funding": {
139 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
140 | }
141 | },
142 | "node_modules/domelementtype": {
143 | "version": "2.3.0",
144 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
145 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
146 | "funding": [
147 | {
148 | "type": "github",
149 | "url": "https://github.com/sponsors/fb55"
150 | }
151 | ]
152 | },
153 | "node_modules/domhandler": {
154 | "version": "5.0.3",
155 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
156 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
157 | "dependencies": {
158 | "domelementtype": "^2.3.0"
159 | },
160 | "engines": {
161 | "node": ">= 4"
162 | },
163 | "funding": {
164 | "url": "https://github.com/fb55/domhandler?sponsor=1"
165 | }
166 | },
167 | "node_modules/domutils": {
168 | "version": "3.1.0",
169 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
170 | "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
171 | "dependencies": {
172 | "dom-serializer": "^2.0.0",
173 | "domelementtype": "^2.3.0",
174 | "domhandler": "^5.0.3"
175 | },
176 | "funding": {
177 | "url": "https://github.com/fb55/domutils?sponsor=1"
178 | }
179 | },
180 | "node_modules/entities": {
181 | "version": "4.5.0",
182 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
183 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
184 | "engines": {
185 | "node": ">=0.12"
186 | },
187 | "funding": {
188 | "url": "https://github.com/fb55/entities?sponsor=1"
189 | }
190 | },
191 | "node_modules/event-target-shim": {
192 | "version": "5.0.1",
193 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
194 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
195 | "engines": {
196 | "node": ">=6"
197 | }
198 | },
199 | "node_modules/fetch-socks": {
200 | "version": "1.3.0",
201 | "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.0.tgz",
202 | "integrity": "sha512-Cq7O53hoNiVeOs6u54f8M/H/w2yzhmnTQ3tcAJj9FNKYOeNGmt8qNU1zpWOzJD09f0uqfmBXxLbzWPsnT6GcRw==",
203 | "dependencies": {
204 | "socks": "^2.8.1",
205 | "undici": "^6.10.1"
206 | }
207 | },
208 | "node_modules/htmlparser2": {
209 | "version": "8.0.2",
210 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
211 | "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
212 | "funding": [
213 | "https://github.com/fb55/htmlparser2?sponsor=1",
214 | {
215 | "type": "github",
216 | "url": "https://github.com/sponsors/fb55"
217 | }
218 | ],
219 | "dependencies": {
220 | "domelementtype": "^2.3.0",
221 | "domhandler": "^5.0.3",
222 | "domutils": "^3.0.1",
223 | "entities": "^4.4.0"
224 | }
225 | },
226 | "node_modules/ip-address": {
227 | "version": "9.0.5",
228 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
229 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
230 | "dependencies": {
231 | "jsbn": "1.1.0",
232 | "sprintf-js": "^1.1.3"
233 | },
234 | "engines": {
235 | "node": ">= 12"
236 | }
237 | },
238 | "node_modules/jsbn": {
239 | "version": "1.1.0",
240 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
241 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
242 | },
243 | "node_modules/lru-cache": {
244 | "version": "10.3.1",
245 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz",
246 | "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g==",
247 | "engines": {
248 | "node": ">=18"
249 | }
250 | },
251 | "node_modules/mri": {
252 | "version": "1.2.0",
253 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
254 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==",
255 | "engines": {
256 | "node": ">=4"
257 | }
258 | },
259 | "node_modules/nth-check": {
260 | "version": "2.1.1",
261 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
262 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
263 | "dependencies": {
264 | "boolbase": "^1.0.0"
265 | },
266 | "funding": {
267 | "url": "https://github.com/fb55/nth-check?sponsor=1"
268 | }
269 | },
270 | "node_modules/p-timeout": {
271 | "version": "4.1.0",
272 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
273 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw==",
274 | "engines": {
275 | "node": ">=10"
276 | }
277 | },
278 | "node_modules/parse5": {
279 | "version": "7.1.2",
280 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
281 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
282 | "dependencies": {
283 | "entities": "^4.4.0"
284 | },
285 | "funding": {
286 | "url": "https://github.com/inikulin/parse5?sponsor=1"
287 | }
288 | },
289 | "node_modules/parse5-htmlparser2-tree-adapter": {
290 | "version": "7.0.0",
291 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
292 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
293 | "dependencies": {
294 | "domhandler": "^5.0.2",
295 | "parse5": "^7.0.0"
296 | },
297 | "funding": {
298 | "url": "https://github.com/inikulin/parse5?sponsor=1"
299 | }
300 | },
301 | "node_modules/safe-compare": {
302 | "version": "1.1.4",
303 | "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz",
304 | "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==",
305 | "dependencies": {
306 | "buffer-alloc": "^1.2.0"
307 | }
308 | },
309 | "node_modules/sandwich-stream": {
310 | "version": "2.0.2",
311 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
312 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==",
313 | "engines": {
314 | "node": ">= 0.10"
315 | }
316 | },
317 | "node_modules/smart-buffer": {
318 | "version": "4.2.0",
319 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
320 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
321 | "engines": {
322 | "node": ">= 6.0.0",
323 | "npm": ">= 3.0.0"
324 | }
325 | },
326 | "node_modules/socks": {
327 | "version": "2.8.1",
328 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz",
329 | "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==",
330 | "dependencies": {
331 | "ip-address": "^9.0.5",
332 | "smart-buffer": "^4.2.0"
333 | },
334 | "engines": {
335 | "node": ">= 10.0.0",
336 | "npm": ">= 3.0.0"
337 | }
338 | },
339 | "node_modules/sprintf-js": {
340 | "version": "1.1.3",
341 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
342 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
343 | },
344 | "node_modules/telegraf": {
345 | "version": "4.16.3",
346 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz",
347 | "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==",
348 | "dependencies": {
349 | "@telegraf/types": "^7.1.0",
350 | "abort-controller": "^3.0.0",
351 | "debug": "^4.3.4",
352 | "mri": "^1.2.0",
353 | "node-fetch": "^2.7.0",
354 | "p-timeout": "^4.1.0",
355 | "safe-compare": "^1.1.4",
356 | "sandwich-stream": "^2.0.2"
357 | },
358 | "bin": {
359 | "telegraf": "lib/cli.mjs"
360 | },
361 | "engines": {
362 | "node": "^12.20.0 || >=14.13.1"
363 | }
364 | },
365 | "node_modules/telegraf/node_modules/debug": {
366 | "version": "4.3.4",
367 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
368 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
369 | "dependencies": {
370 | "ms": "2.1.2"
371 | },
372 | "engines": {
373 | "node": ">=6.0"
374 | },
375 | "peerDependenciesMeta": {
376 | "supports-color": {
377 | "optional": true
378 | }
379 | }
380 | },
381 | "node_modules/telegraf/node_modules/ms": {
382 | "version": "2.1.2",
383 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
384 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
385 | },
386 | "node_modules/telegraf/node_modules/node-fetch": {
387 | "version": "2.7.0",
388 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
389 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
390 | "dependencies": {
391 | "whatwg-url": "^5.0.0"
392 | },
393 | "engines": {
394 | "node": "4.x || >=6.0.0"
395 | },
396 | "peerDependencies": {
397 | "encoding": "^0.1.0"
398 | },
399 | "peerDependenciesMeta": {
400 | "encoding": {
401 | "optional": true
402 | }
403 | }
404 | },
405 | "node_modules/tr46": {
406 | "version": "0.0.3",
407 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
408 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
409 | },
410 | "node_modules/tslib": {
411 | "version": "2.6.2",
412 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
413 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
414 | },
415 | "node_modules/undici": {
416 | "version": "6.13.0",
417 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz",
418 | "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw==",
419 | "engines": {
420 | "node": ">=18.0"
421 | }
422 | },
423 | "node_modules/webidl-conversions": {
424 | "version": "3.0.1",
425 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
426 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
427 | },
428 | "node_modules/whatwg-url": {
429 | "version": "5.0.0",
430 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
431 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
432 | "dependencies": {
433 | "tr46": "~0.0.3",
434 | "webidl-conversions": "^3.0.0"
435 | }
436 | }
437 | },
438 | "dependencies": {
439 | "@telegraf/types": {
440 | "version": "7.1.0",
441 | "resolved": "https://registry.npmjs.org/@telegraf/types/-/types-7.1.0.tgz",
442 | "integrity": "sha512-kGevOIbpMcIlCDeorKGpwZmdH7kHbqlk/Yj6dEpJMKEQw5lk0KVQY0OLXaCswy8GqlIVLd5625OB+rAntP9xVw=="
443 | },
444 | "abort-controller": {
445 | "version": "3.0.0",
446 | "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
447 | "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
448 | "requires": {
449 | "event-target-shim": "^5.0.0"
450 | }
451 | },
452 | "async-mutex": {
453 | "version": "0.5.0",
454 | "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz",
455 | "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==",
456 | "requires": {
457 | "tslib": "^2.4.0"
458 | }
459 | },
460 | "boolbase": {
461 | "version": "1.0.0",
462 | "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
463 | "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
464 | },
465 | "buffer-alloc": {
466 | "version": "1.2.0",
467 | "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz",
468 | "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==",
469 | "requires": {
470 | "buffer-alloc-unsafe": "^1.1.0",
471 | "buffer-fill": "^1.0.0"
472 | }
473 | },
474 | "buffer-alloc-unsafe": {
475 | "version": "1.1.0",
476 | "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz",
477 | "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg=="
478 | },
479 | "buffer-fill": {
480 | "version": "1.0.0",
481 | "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz",
482 | "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw="
483 | },
484 | "cheerio": {
485 | "version": "1.0.0-rc.12",
486 | "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.12.tgz",
487 | "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
488 | "requires": {
489 | "cheerio-select": "^2.1.0",
490 | "dom-serializer": "^2.0.0",
491 | "domhandler": "^5.0.3",
492 | "domutils": "^3.0.1",
493 | "htmlparser2": "^8.0.1",
494 | "parse5": "^7.0.0",
495 | "parse5-htmlparser2-tree-adapter": "^7.0.0"
496 | }
497 | },
498 | "cheerio-select": {
499 | "version": "2.1.0",
500 | "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
501 | "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
502 | "requires": {
503 | "boolbase": "^1.0.0",
504 | "css-select": "^5.1.0",
505 | "css-what": "^6.1.0",
506 | "domelementtype": "^2.3.0",
507 | "domhandler": "^5.0.3",
508 | "domutils": "^3.0.1"
509 | }
510 | },
511 | "css-select": {
512 | "version": "5.1.0",
513 | "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
514 | "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
515 | "requires": {
516 | "boolbase": "^1.0.0",
517 | "css-what": "^6.1.0",
518 | "domhandler": "^5.0.2",
519 | "domutils": "^3.0.1",
520 | "nth-check": "^2.0.1"
521 | }
522 | },
523 | "css-what": {
524 | "version": "6.1.0",
525 | "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
526 | "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw=="
527 | },
528 | "dom-serializer": {
529 | "version": "2.0.0",
530 | "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
531 | "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
532 | "requires": {
533 | "domelementtype": "^2.3.0",
534 | "domhandler": "^5.0.2",
535 | "entities": "^4.2.0"
536 | }
537 | },
538 | "domelementtype": {
539 | "version": "2.3.0",
540 | "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz",
541 | "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="
542 | },
543 | "domhandler": {
544 | "version": "5.0.3",
545 | "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
546 | "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
547 | "requires": {
548 | "domelementtype": "^2.3.0"
549 | }
550 | },
551 | "domutils": {
552 | "version": "3.1.0",
553 | "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
554 | "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
555 | "requires": {
556 | "dom-serializer": "^2.0.0",
557 | "domelementtype": "^2.3.0",
558 | "domhandler": "^5.0.3"
559 | }
560 | },
561 | "entities": {
562 | "version": "4.5.0",
563 | "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
564 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="
565 | },
566 | "event-target-shim": {
567 | "version": "5.0.1",
568 | "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
569 | "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="
570 | },
571 | "fetch-socks": {
572 | "version": "1.3.0",
573 | "resolved": "https://registry.npmjs.org/fetch-socks/-/fetch-socks-1.3.0.tgz",
574 | "integrity": "sha512-Cq7O53hoNiVeOs6u54f8M/H/w2yzhmnTQ3tcAJj9FNKYOeNGmt8qNU1zpWOzJD09f0uqfmBXxLbzWPsnT6GcRw==",
575 | "requires": {
576 | "socks": "^2.8.1",
577 | "undici": "^6.10.1"
578 | }
579 | },
580 | "htmlparser2": {
581 | "version": "8.0.2",
582 | "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz",
583 | "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
584 | "requires": {
585 | "domelementtype": "^2.3.0",
586 | "domhandler": "^5.0.3",
587 | "domutils": "^3.0.1",
588 | "entities": "^4.4.0"
589 | }
590 | },
591 | "ip-address": {
592 | "version": "9.0.5",
593 | "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz",
594 | "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==",
595 | "requires": {
596 | "jsbn": "1.1.0",
597 | "sprintf-js": "^1.1.3"
598 | }
599 | },
600 | "jsbn": {
601 | "version": "1.1.0",
602 | "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz",
603 | "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A=="
604 | },
605 | "lru-cache": {
606 | "version": "10.3.1",
607 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.1.tgz",
608 | "integrity": "sha512-9/8QXrtbGeMB6LxwQd4x1tIMnsmUxMvIH/qWGsccz6bt9Uln3S+sgAaqfQNhbGA8ufzs2fHuP/yqapGgP9Hh2g=="
609 | },
610 | "mri": {
611 | "version": "1.2.0",
612 | "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz",
613 | "integrity": "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="
614 | },
615 | "nth-check": {
616 | "version": "2.1.1",
617 | "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
618 | "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
619 | "requires": {
620 | "boolbase": "^1.0.0"
621 | }
622 | },
623 | "p-timeout": {
624 | "version": "4.1.0",
625 | "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-4.1.0.tgz",
626 | "integrity": "sha512-+/wmHtzJuWii1sXn3HCuH/FTwGhrp4tmJTxSKJbfS+vkipci6osxXM5mY0jUiRzWKMTgUT8l7HFbeSwZAynqHw=="
627 | },
628 | "parse5": {
629 | "version": "7.1.2",
630 | "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
631 | "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
632 | "requires": {
633 | "entities": "^4.4.0"
634 | }
635 | },
636 | "parse5-htmlparser2-tree-adapter": {
637 | "version": "7.0.0",
638 | "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
639 | "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
640 | "requires": {
641 | "domhandler": "^5.0.2",
642 | "parse5": "^7.0.0"
643 | }
644 | },
645 | "safe-compare": {
646 | "version": "1.1.4",
647 | "resolved": "https://registry.npmjs.org/safe-compare/-/safe-compare-1.1.4.tgz",
648 | "integrity": "sha512-b9wZ986HHCo/HbKrRpBJb2kqXMK9CEWIE1egeEvZsYn69ay3kdfl9nG3RyOcR+jInTDf7a86WQ1d4VJX7goSSQ==",
649 | "requires": {
650 | "buffer-alloc": "^1.2.0"
651 | }
652 | },
653 | "sandwich-stream": {
654 | "version": "2.0.2",
655 | "resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
656 | "integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ=="
657 | },
658 | "smart-buffer": {
659 | "version": "4.2.0",
660 | "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
661 | "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="
662 | },
663 | "socks": {
664 | "version": "2.8.1",
665 | "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.1.tgz",
666 | "integrity": "sha512-B6w7tkwNid7ToxjZ08rQMT8M9BJAf8DKx8Ft4NivzH0zBUfd6jldGcisJn/RLgxcX3FPNDdNQCUEMMT79b+oCQ==",
667 | "requires": {
668 | "ip-address": "^9.0.5",
669 | "smart-buffer": "^4.2.0"
670 | }
671 | },
672 | "sprintf-js": {
673 | "version": "1.1.3",
674 | "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
675 | "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="
676 | },
677 | "telegraf": {
678 | "version": "4.16.3",
679 | "resolved": "https://registry.npmjs.org/telegraf/-/telegraf-4.16.3.tgz",
680 | "integrity": "sha512-yjEu2NwkHlXu0OARWoNhJlIjX09dRktiMQFsM678BAH/PEPVwctzL67+tvXqLCRQQvm3SDtki2saGO9hLlz68w==",
681 | "requires": {
682 | "@telegraf/types": "^7.1.0",
683 | "abort-controller": "^3.0.0",
684 | "debug": "^4.3.4",
685 | "mri": "^1.2.0",
686 | "node-fetch": "^2.7.0",
687 | "p-timeout": "^4.1.0",
688 | "safe-compare": "^1.1.4",
689 | "sandwich-stream": "^2.0.2"
690 | },
691 | "dependencies": {
692 | "debug": {
693 | "version": "4.3.4",
694 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
695 | "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
696 | "requires": {
697 | "ms": "2.1.2"
698 | }
699 | },
700 | "ms": {
701 | "version": "2.1.2",
702 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
703 | "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
704 | },
705 | "node-fetch": {
706 | "version": "2.7.0",
707 | "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
708 | "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
709 | "requires": {
710 | "whatwg-url": "^5.0.0"
711 | }
712 | }
713 | }
714 | },
715 | "tr46": {
716 | "version": "0.0.3",
717 | "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz",
718 | "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="
719 | },
720 | "tslib": {
721 | "version": "2.6.2",
722 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
723 | "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
724 | },
725 | "undici": {
726 | "version": "6.13.0",
727 | "resolved": "https://registry.npmjs.org/undici/-/undici-6.13.0.tgz",
728 | "integrity": "sha512-Q2rtqmZWrbP8nePMq7mOJIN98M0fYvSgV89vwl/BQRT4mDOeY2GXZngfGpcBBhtky3woM7G24wZV3Q304Bv6cw=="
729 | },
730 | "webidl-conversions": {
731 | "version": "3.0.1",
732 | "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
733 | "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="
734 | },
735 | "whatwg-url": {
736 | "version": "5.0.0",
737 | "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz",
738 | "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==",
739 | "requires": {
740 | "tr46": "~0.0.3",
741 | "webidl-conversions": "^3.0.0"
742 | }
743 | }
744 | }
745 | }
746 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "saucenaobot",
3 | "version": "1.0.0",
4 | "description": "Reverse Image Search bot for Telegram. Check settings/private.js for the api keys and tokens required.",
5 | "main": "bot/init.js",
6 | "type": "module",
7 | "scripts": {
8 | "test": "echo \"Error: no test specified\" && exit 1",
9 | "start": "node bot/init.js"
10 | },
11 | "author": "kawaiiDango",
12 | "bugs": {
13 | "url": "https://github.com/kawaiiDango/reverseSearchBot/issues"
14 | },
15 | "repository": "kawaiiDango/reverseSearchBot",
16 | "license": "ISC",
17 | "dependencies": {
18 | "async-mutex": "^0.5.0",
19 | "cheerio": "^1.0.0-rc.12",
20 | "lru-cache": "^10.3.1",
21 | "fetch-socks": "^1.3.0",
22 | "telegraf": "^4.16.3"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/settings/private.js.example:
--------------------------------------------------------------------------------
1 | export const privateSettings = {
2 | botToken: "tg bot token",
3 | analKey: "amplitude.com analytics key",
4 | botName: "mybot",
5 | webhookPort:443,
6 | webhookHost: "127.0.0.1",
7 | webhookEndpoint: "https://my.site/secretPath",
8 | socksProxyHost: "0.0.0.0",
9 | socksProxyPort: 1234,
10 | changeProxyCommand: "",
11 | favouriteGroups: [
12 | -111111
13 | ]
14 | }
--------------------------------------------------------------------------------
/settings/settings.js:
--------------------------------------------------------------------------------
1 | import { privateSettings as _privateSettings } from "./private.js";
2 |
3 | export const privateSettings = _privateSettings;
4 |
5 | export const urls = {
6 | sauceNao: "https://saucenao.com/search.php?",
7 | mal: "https://myanimelist.net/anime.php?",
8 | analUrl: "https://api2.amplitude.com/2/httpapi",
9 | pixiv_id: "https://www.pixiv.net/member_illust.php?mode=medium&illust_id=",
10 | danbooru_id: "https://danbooru.donmai.us/post/show/",
11 | gelbooru_id: "https://gelbooru.com/index.php?page=post&s=view&id=",
12 | sankaku_id: "https://chan.sankakucomplex.com/post/show/",
13 | "anime-pictures_id": "https://anime-pictures.net/pictures/view_post/",
14 | imdb_id: "https://www.imdb.com/title/",
15 | anidb_aid: "https://anidb.net/perl-bin/animedb.pl?show=anime&aid=",
16 | e621_id: "https://e621.net/post/show/",
17 | yandere_id: "https://yande.re/post/show/",
18 | drawr_id: "https://drawr.net/show.php?id=",
19 | da_id: "https://deviantart.com/view/",
20 | pawoo_id: "https://pawoo.net/@",
21 | bcy_id: "https://bcy.net/",
22 | url: "",
23 | /* wont add:
24 | seiga_id
25 | */
26 | };
27 |
28 | export const id_buttonName = {
29 | pixiv_id: "Pixiv",
30 | danbooru_id: "Danbooru",
31 | gelbooru_id: "Gelbooru",
32 | sankaku_id: "Sankaku",
33 | "anime-pictures_id": "Anime-Pictures",
34 | imdb_id: "IMDb",
35 | anidb_aid: "AniDB",
36 | e621_id: "e621",
37 | yandere_id: "yandere",
38 | drawr_id: "Drawr",
39 | da_id: "deviantArt",
40 | pawoo_id: "Pawoo",
41 | bcy_id: "BCY",
42 | url: "",
43 |
44 | loading: "🍝",
45 | };
46 |
47 | export const msgs = {
48 | loading: "Pouring some sauce on it...",
49 | zeroResult:
50 | "No sauce found; this bot works only with uncropped anime/2d art.",
51 | help:
52 | "Send me an image, a sticker, an image file or a GIF to find its source Right Nao.\n\n" +
53 | "If I'm in a group, reply to a media with /sauce@" +
54 | privateSettings.botName +
55 | " or /source@" +
56 | privateSettings.botName +
57 | "",
58 | reachLimitation: "The request limit has reached, try again after some time.",
59 | unknownError: "Oopsie doopsie, I did a fucky wucky",
60 | invalidFileId: "Invalid file",
61 | keywordHelp:
62 | "Don't just click me like that. \n\nPM me a pic, GIF, a sticker, or an image file\n\nIf I'm in a group, reply with /source@" +
63 | privateSettings.botName +
64 | " or /sauce@" +
65 | privateSettings.botName +
66 | " to find its source.",
67 | privacyPolicy:
68 | "The bot Reverse Search Bot, counts unique telegram user IDs for analytics.",
69 | };
70 |
71 | export const floodProtect = {
72 | message: "aaaaah, slow down...",
73 | interval: 30,
74 | msgLimit: 3,
75 | };
76 |
77 | export const sauceNaoParams = {
78 | db: 999,
79 | // output_type: 2,
80 | testmode: 1,
81 | numres: 5,
82 | minSimilarity: 60,
83 | tolerance: 7,
84 | };
85 |
86 | export const keywords = /^(sauce|source|what\?)$/i;
87 | export const commands = [
88 | "sauce",
89 | "source",
90 | "sauce@" + privateSettings.botName,
91 | "source@" + privateSettings.botName,
92 | ];
93 | export const userAgent =
94 | "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_4_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4.1 Safari/605.1.15";
95 |
--------------------------------------------------------------------------------
/tools/tools.js:
--------------------------------------------------------------------------------
1 | export async function editMessageText(bot, msg, text, extras) {
2 | try {
3 | if (msg.inline_message_id)
4 | await bot.telegram.editMessageText(
5 | undefined,
6 | undefined,
7 | msg.inline_message_id,
8 | text,
9 | extras
10 | );
11 | else
12 | await bot.telegram.editMessageText(
13 | msg.chat.id,
14 | msg.message_id,
15 | undefined,
16 | text,
17 | extras
18 | );
19 | } catch (e) {
20 | console.log("editMessageText failed");
21 | }
22 | }
23 | export function json2query(params) {
24 | const query = Object.keys(params)
25 | .map((k) => encodeURIComponent(k) + "=" + encodeURIComponent(params[k]))
26 | .join("&");
27 | return query;
28 | }
29 |
30 | export function isSupportedExt(filename) {
31 | if (!filename) return false;
32 | const ext = filename.substr(filename.lastIndexOf(".") + 1).toLowerCase();
33 | if (
34 | ext == "jpg" ||
35 | ext == "jpeg" ||
36 | ext == "gif" ||
37 | ext == "png" ||
38 | ext == "webp" ||
39 | ext == "bmp"
40 | )
41 | return true;
42 | return false;
43 | }
44 |
--------------------------------------------------------------------------------