├── LICENSE
├── README.md
├── SillyDev
├── README.md
├── config.json
├── index.js
└── package.json
├── bark-server
├── index.js
└── package.json
└── ddddocr
├── README.md
├── main.py
└── requirements.txt
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Sliverkiss
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Awesome Serverless 
4 |
5 | 本仓库收录基于Serverless服务的开源工具,为独立开发者早期摸索期提供一个省心省时的工具集,持续整理中……
6 |
7 |
8 | **收录标准:**
9 |
10 | - 帮助但不限于独立开发者提升开发效率
11 | - 帮助但不限于独立开发者降低成本
12 | - 足够简单便捷
13 |
14 | 欢迎提 pr 和 issues 更新。 部署或操作过程中有任何问题可以提issue或者私信咨询~
15 |
16 | ## Python
17 |
18 | | 名称 | 特性 |在线地址 | 状态|
19 | | --- | --- | --- |---|
20 | | ddddocr |滑块识别 | | 有效中|
21 |
22 | ## Node.js
23 |
24 | | 名称 | 特性 |在线地址 | 状态|
25 | | --- | --- | --- |---|
26 | | SillyDev |silly自动刷积分 | | 有效中|
27 | | bark-server |bark推送服务器搭建 | | 有效中|
28 |
29 |
30 | ## Star History
31 |
32 | [](https://star-history.com/#sliverkiss/serverless&Timeline)
33 |
--------------------------------------------------------------------------------
/SillyDev/README.md:
--------------------------------------------------------------------------------
1 | # SillyDev
2 | 在 Silly 上部署 ```Node服务``` 自动获取```Silly Development```积分、并通过telegram bot管理信息、兑换资源及服务器续期。如果本项目对你有所帮助的话,不妨点个Star⭐️
3 |
4 | ### 一、准备材料
5 |
6 | 1. 一枚邮箱,不建议使用qq邮箱。
7 |
8 | ### 二、部署服务。
9 | 1. 打开 Silly 的网站:https://panel.sillydevelopment.co.uk ,然后点击里面的“Signup with email”进行注册。如有账户直接登录即可。
10 |
11 |
12 |
13 | 2. 打开自己的邮件,然后点击“Verify email”验证邮箱.
14 |
15 |
16 |
17 | 3. 点击左侧栏的编辑按钮,然后点击“Create”进行创建
18 |
19 |
20 |
21 | 4. 可以使用最小配置(Cpu选择50,RAM选择512,Storage选择512),选择地区随意选择,然后 Nest 选择“Code Languages”,Egg 选择“Node.js”,最后点击“Create”按钮,创建服务器。
22 |
23 |
24 |
25 |
26 |
27 | 5. 等待服务器安装的时候,可以将本仓库的内容下载项目文件下来。
28 |
29 |
30 |
31 | 6. 将除了 ```README.md``` 和 ```LICENSE``` 文件外的所有文件,上传至服务器上
32 |
33 |
34 |
35 | 7. 根据下面表格信息,修改```conf.json```内容
36 |
37 | | 变量名 | 是否必须 | 默认 | 备注 |
38 | | :----: | :--: |:--: | -------: |
39 | | tgBotToken | Yes | | 可以通过bot来管理面板 |
40 | | X-XSRF-Token | Yes | | 自行通过抓包获取 |
41 | | Cookie | Yes | | 自行通过抓包获取 |
42 | | User-Agent | No | | 自行通过抓包获取 |
43 |
44 | > Bot指令如下:
45 |
46 | | 命令 | 说明 |
47 | | :----: | :--: |
48 | | ```/start``` | 开始 |
49 | | ```/help``` | 查看菜单 |
50 | | ```/info``` | 查看Silly个人信息 |
51 | | ```/server``` | 查看当前服务器信息 |
52 | | ```/renew``` | 服务器续期 |
53 | | ```/resources``` | 兑换服务器资源 |
54 |
55 |
56 | 8. 转到“Console”页面,点击“Start”按钮,即可食用。
57 |
58 |
--------------------------------------------------------------------------------
/SillyDev/config.json:
--------------------------------------------------------------------------------
1 | {
2 | "tgBotToken":"",
3 | "X-XSRF-Token":"",
4 | "Cookie":"",
5 | "User-Agent":""
6 | }
7 |
--------------------------------------------------------------------------------
/SillyDev/index.js:
--------------------------------------------------------------------------------
1 | const express = require("express");
2 | const TelegramBot = require("node-telegram-bot-api");
3 | const {
4 | tgBotToken,
5 | "X-XSRF-Token": token,
6 | Cookie: ck,
7 | "User-Agent": ua,
8 | } = require("./config.json");
9 | const SERVER = process.env.SERVER_IP;
10 | const PORT = process.env.SERVER_PORT;
11 | const app = express();
12 |
13 | const bot = new TelegramBot(tgBotToken, { polling: true });
14 |
15 | bot.onText(/\/start/, (msg) => {
16 | bot.sendMessage(
17 | msg.chat.id,
18 | "Welcome to Silly Bot. Use /help to see available commands."
19 | );
20 | });
21 |
22 | // Listen for /help command
23 | bot.onText(/\/help/, (msg) => {
24 | bot.sendMessage(
25 | msg.chat.id,
26 | "Available commands:\n" +
27 | "/info - 查看Silly个人信息\n" +
28 | "/server - 查看当前服务器信息\n" +
29 | "/renew - 服务器续期\n" +
30 | "/resources - 兑换服务器资源"
31 | );
32 | });
33 |
34 | bot.onText(/\/info/, async (msg) => {
35 | try {
36 | const escapeMarkdown = (text) => {
37 | return text
38 | .toString()
39 | .replace(/([\_\*\[\]\(\)\~\`\>\#\+\-\=\|\{\}\.\!])/g, "\\$1");
40 | };
41 | // 计算每列的最大宽度
42 | const calculateColumnWidths = (keys, values) => {
43 | let keyWidth = Math.max(...keys.map((key) => key.length));
44 | let valueWidth = Math.max(
45 | ...values.map((value) => value.toString().length)
46 | );
47 | return [keyWidth, valueWidth];
48 | };
49 |
50 | // 格式化 JSON 为两列多行 Markdown 表格
51 | const formatJsonToTable = (data) => {
52 | const keys = Object.keys(data);
53 | const values = Object.values(data);
54 | const [keyWidth, valueWidth] = calculateColumnWidths(keys, values);
55 |
56 | let formattedTable = "";
57 |
58 | // 打印表头
59 | formattedTable += `| ${"Key".padEnd(keyWidth)} | ${"Value".padEnd(
60 | valueWidth
61 | )} |\n`;
62 | formattedTable += `| ${"-".repeat(keyWidth)} | ${"-".repeat(
63 | valueWidth
64 | )} |\n`; // 表头分隔线
65 |
66 | // 打印数据行
67 | keys.forEach((key, index) => {
68 | const escapedKey = escapeMarkdown(key).padEnd(keyWidth);
69 | const escapedValue = escapeMarkdown(
70 | values[index].toString()
71 | ).padEnd(valueWidth);
72 | formattedTable += `| ${escapedKey} | ${escapedValue} |\n`;
73 | });
74 |
75 | return formattedTable;
76 | };
77 | let response = await get("/api/client/store");
78 | const chatId = msg.chat.id;
79 | const tableMessage = formatJsonToTable(response?.attributes);
80 |
81 | bot.sendMessage(
82 | chatId,
83 | "查询成功!当前可用资源如下:\n" +
84 | `\`\`\`\n${tableMessage}\n\`\`\``,
85 | { parse_mode: "MarkdownV2" }
86 | );
87 | } catch (e) {
88 | bot.sendMessage(msg.chat.id, `Error:${e}`);
89 | }
90 | });
91 |
92 | bot.onText(/\/server/, async (msg) => {
93 | try {
94 | let response = await get("/api/client?page=1");
95 | let total = response.meta.pagination.total;
96 | let serverList = response?.data
97 | ?.map((e) => {
98 | let o = e.attributes;
99 | return {
100 | name: o.name,
101 | serverId: o.uuid,
102 | renewal: o.renewal,
103 | status: o.status,
104 | };
105 | })
106 | .filter((e) => e.status != "suspended");
107 | Promise.all(
108 | serverList.map((e) => {
109 | const text = `*${e.name}*\nUUID:${e.serverId}\n到期天数:${e.renewal}`;
110 | return bot.sendMessage(msg.chat.id, text, {
111 | parse_mode: "Markdown",
112 | });
113 | })
114 | );
115 | bot.sendMessage(
116 | msg.chat.id,
117 | `共${total}台服务器,可用数量:${serverList.length}`
118 | );
119 | } catch (e) {
120 | bot.sendMessage(msg.chat.id, `Error:${e}`);
121 | }
122 | });
123 |
124 | bot.onText(/\/renew/, async (msg) => {
125 | try {
126 | let response = await get("/api/client?page=1");
127 | let arr = [[{ text: "全部", callback_data: "all" }]];
128 | response?.data?.map((e) => {
129 | let o = e.attributes;
130 | if (o.status != "suspended")
131 | arr.push([{ text: o.name, callback_data: o.name }]);
132 | });
133 | const options = {
134 | reply_markup: {
135 | inline_keyboard: arr,
136 | },
137 | };
138 | bot.sendMessage(msg.chat.id, "请选择要续期的服务器:", options);
139 | } catch (e) {
140 | bot.sendMessage(msg.chat.id, `Error:${e}`);
141 | }
142 | });
143 |
144 | bot.onText(/\/resources/, async (msg) => {
145 | try {
146 | let arr = [
147 | [
148 | { text: "balance", callback_data: "balance" },
149 | { text: "cpu", callback_data: "cpu" },
150 | ],
151 | [
152 | { text: "memory", callback_data: "memory" },
153 | { text: "disk", callback_data: "disk" },
154 | ],
155 | [
156 | { text: "slots", callback_data: "slots" },
157 | { text: "ports", callback_data: "ports" },
158 | ],
159 | [
160 | { text: "backups", callback_data: "backups" },
161 | { text: "databases", callback_data: "databases" },
162 | ],
163 | ];
164 | const options = {
165 | reply_markup: {
166 | inline_keyboard: arr,
167 | },
168 | };
169 | bot.sendMessage(msg.chat.id, "请选择要兑换的资源:", options);
170 | } catch (e) {
171 | bot.sendMessage(msg.chat.id, `Error:${e}`);
172 | }
173 | });
174 |
175 | // 处理按钮点击事件
176 | bot.on("callback_query", async (callbackQuery) => {
177 | const msg = callbackQuery.message;
178 | const data = callbackQuery.data;
179 | let response = await get("/api/client?page=1");
180 | let serverList = response?.data
181 | ?.map((e) => {
182 | let o = e.attributes;
183 | return {
184 | name: o.name,
185 | serverId: o.uuid,
186 | renewal: o.renewal,
187 | status: o.status,
188 | };
189 | })
190 | .filter((e) => e.status != "suspended");
191 | switch (data) {
192 | case "all":
193 | await Promise.all(
194 | serverList.map((e) =>
195 | post(`/api/client/servers/${e.serverId}/renew`)
196 | )
197 | );
198 | let message = [];
199 | serverList.map((e) => message.push(`*${e.name}*`));
200 | bot.sendMessage(
201 | msg.chat.id,
202 | `${message.join("、")}续期成功🎉🎉🎉`,
203 | { parse_mode: "Markdown" }
204 | );
205 | break;
206 | case "balance":
207 | case "cpu":
208 | case "memory":
209 | case "disk":
210 | case "slots":
211 | case "ports":
212 | case "backups":
213 | case "databases":
214 | await post(`/api/client/store/resources`, { resource: data });
215 | bot.sendMessage(msg.chat.id, `兑换*${data}*资源成功🎉🎉🎉`, {
216 | parse_mode: "Markdown",
217 | });
218 | break;
219 | default:
220 | let server = serverList.find((e) => e.name == data);
221 | await post(`/api/client/servers/${server.serverId}/renew`);
222 | bot.sendMessage(msg.chat.id, `*${server.name}*续期成功🎉🎉🎉`, {
223 | parse_mode: "Markdown",
224 | });
225 | break;
226 | }
227 | });
228 |
229 | //首页显示内容
230 | app.get("/", function (req, res) {
231 | res.send("hello world");
232 | });
233 |
234 | app.get("/test", async function (req, res) {
235 | let response = await get("/api/client?page=1");
236 | res.send(response);
237 | });
238 |
239 | // keepalive begin
240 | async function keep_web_alive() {
241 | try {
242 | const response = await fetch(`http://${SERVER}:${PORT}`);
243 | const body = await response.text();
244 | if (!response.ok) {
245 | throw new Error(`保活-请求主页-命令行执行错误: ${response.status}`);
246 | }
247 | console.log(`保活-请求主页-命令行执行成功,响应报文: ${body}`);
248 | } catch (error) {
249 | console.error(`保活-请求主页-命令行执行错误: ${error.message}`);
250 | }
251 | }
252 |
253 | async function getEarn() {
254 | console.log("开始执行服务器获取积分任务...");
255 | await post("/api/client/store/creditearning");
256 | }
257 |
258 | //--—-----------------任务定时配置---—--------------
259 | //定时自动保活,每10秒执行一次
260 | setInterval(keep_web_alive, 10e3);
261 | //定时自动获取积分,每分钟执行一次
262 | setInterval(getEarn, 6e4);
263 |
264 | //--—-----------------辅助函数区域---—--------------
265 | //封装请求方法
266 | async function get(api) {
267 | const res = await fetch(`https://panel.sillydevelopment.co.uk${api}`, {
268 | headers: {
269 | Accept: "application/json",
270 | "Accept-Encoding": "gzip, deflate, br",
271 | "Accept-Language": "zh-CN,zh-Hans;q=0.9",
272 | Connection: "keep-alive",
273 | Cookie: ck,
274 | Host: "panel.sillydevelopment.co.uk",
275 | Referer: "https://panel.sillydevelopment.co.uk/",
276 | "Sec-Fetch-Mode": "cors",
277 | "Sec-Fetch-Dest": "empty",
278 | "Sec-Fetch-Site": "same-origin",
279 | "X-Requested-With": "XMLHttpRequest",
280 | "X-XSRF-TOKEN": token,
281 | "User-Agent":
282 | ua ||
283 | "Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/111.0.5563.101 Mobile/15E148 Safari/604.1",
284 | },
285 | }).then((res) => res.json());
286 | return res;
287 | }
288 |
289 | async function post(api, data = {}) {
290 | const url = `https://panel.sillydevelopment.co.uk${api}`;
291 | const headers = {
292 | Accept: "application/json",
293 | "Accept-Encoding": "gzip, deflate, br",
294 | "Accept-Language": "zh-CN,zh-Hans;q=0.9",
295 | Connection: "keep-alive",
296 | "Content-Type": "application/json",
297 | Cookie: ck,
298 | Host: "panel.sillydevelopment.co.uk",
299 | Origin: "https://panel.sillydevelopment.co.uk",
300 | "Sec-Fetch-Mode": "cors",
301 | "Sec-Fetch-Dest": "empty",
302 | "Sec-Fetch-Site": "same-origin",
303 | "User-Agent":
304 | ua ||
305 | `Mozilla/5.0 (iPad; CPU OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/111.0.5563.101 Mobile/15E148 Safari/604.1`,
306 | "X-Requested-With": "XMLHttpRequest",
307 | "X-XSRF-TOKEN": token,
308 | };
309 |
310 | try {
311 | const response = await fetch(url, {
312 | method: "POST",
313 | headers: headers,
314 | body: JSON.stringify(data),
315 | });
316 | if (response.status === 204) {
317 | console.log(`Request successful: ${response.status} => 调用成功!`);
318 | } else if (response.status === 429) {
319 | console.error(
320 | `Error: ${response.status} => 请求过于频繁,请稍后再试!`
321 | );
322 | } else {
323 | console.error(`Error: ${response.status} => 调用失败!`);
324 | }
325 | } catch (error) {
326 | console.error(`Fetch error: ${error.message}`);
327 | }
328 | }
329 |
330 | //--—-----------------服务器启动配置---—--------------
331 | // 启动服务器
332 | app.listen(PORT, () => {
333 | console.log(`Server is listening on http://${SERVER}:${PORT}`);
334 | });
335 |
--------------------------------------------------------------------------------
/SillyDev/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "express-hello-world",
3 | "version": "1.0.0",
4 | "description": "Express Hello World",
5 | "main": "index.js",
6 | "repository": "",
7 | "author": "sliverkiss",
8 | "license": "AGPL-3.0",
9 | "private": false,
10 | "scripts": {
11 | "start": "node index.js"
12 | },
13 | "dependencies": {
14 | "express": "^4.18.2",
15 | "http-proxy-middleware": "^2.0.6",
16 | "node-telegram-bot-api": "^0.65.1",
17 | "request": "^2.88.2"
18 | },
19 | "engines": {
20 | "node": ">=14"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/bark-server/index.js:
--------------------------------------------------------------------------------
1 | const { APNS, Notification, Errors } = require("apns2");
2 | const Koa = require("koa");
3 | const Router = require("@koa/router");
4 | const sqlite3 = require("sqlite3").verbose();
5 | const bodyParser = require("koa-bodyparser");
6 | const meta = require("./package.json");
7 |
8 | /**
9 | * Cert info from: https://github.com/Finb/bark-server/blob/master/deploy/AuthKey_LH4T9V5U4R_5U8LBRXG3A.p8
10 | */
11 | const APN_KEY = "LH4T9V5U4R";
12 | const TEAM_ID = "5U8LBRXG3A";
13 | const CERT = `
14 | -----BEGIN PRIVATE KEY-----
15 | MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQg4vtC3g5L5HgKGJ2+
16 | T1eA0tOivREvEAY2g+juRXJkYL2gCgYIKoZIzj0DAQehRANCAASmOs3JkSyoGEWZ
17 | sUGxFs/4pw1rIlSV2IC19M8u3G5kq36upOwyFWj9Gi3Ejc9d3sC7+SHRqXrEAJow
18 | 8/7tRpV+
19 | -----END PRIVATE KEY-----
20 | `;
21 |
22 | const app = new Koa();
23 | const router = new Router();
24 | const db = new sqlite3.Database("devices.db");
25 | const client = new APNS({
26 | team: TEAM_ID,
27 | keyId: APN_KEY,
28 | signingKey: CERT,
29 | defaultTopic: "me.fin.bark"
30 | });
31 |
32 | async function ensureInitDb() {
33 | db.run("CREATE TABLE IF NOT EXISTS token (key TEXT NOT NULL PRIMARY KEY, token TEXT)");
34 | }
35 |
36 | async function putDeviceToken(key, deviceToken) {
37 | db.run("REPLACE INTO token (key, token) VALUES(?, ?)", key, deviceToken);
38 | }
39 |
40 | async function getDeviceToken(key) {
41 | return new Promise((resolve, reject) => {
42 | db.get("SELECT * FROM token WHERE key = ?", key, (err, row) => {
43 | if (err) {
44 | reject(err);
45 | } else if (row && row.token) {
46 | resolve(row.token);
47 | } else {
48 | resolve(null);
49 | }
50 | });
51 | });
52 | }
53 |
54 | async function ping(ctx) {
55 | ctx.body = {
56 | "code": 200,
57 | "data": {
58 | "version": meta.version
59 | },
60 | "message": "pong"
61 | };
62 | }
63 |
64 | async function register(ctx) {
65 | let { key, deviceToken } = ctx.query;
66 | if (key && deviceToken) {
67 | await putDeviceToken(key, deviceToken);
68 | ctx.body = {
69 | "code": 200,
70 | "data": {
71 | key, deviceToken
72 | },
73 | "message": "Registration successful"
74 | };
75 | } else {
76 | ctx.body = { "code": 400, "message": "Wrong query params" };
77 | }
78 | }
79 |
80 | /**
81 | * Support both query strings or request body
82 | */
83 | function getParamCompat(ctx, paramName, fallback) {
84 | if (ctx.query[paramName]) {
85 | return ctx.query[paramName];
86 | }
87 | if (ctx.request.body && ctx.request.body[paramName]) {
88 | return ctx.request.body[paramName];
89 | }
90 | return fallback;
91 | }
92 |
93 | async function send(ctx) {
94 | let { key, title, body } = ctx.params;
95 | if (!key) {
96 | ctx.body = { "code": 400, "message": "Wrong path params" };
97 | return;
98 | }
99 | let deviceToken = await getDeviceToken(key);
100 | if (!deviceToken) {
101 | ctx.body = { "code": 400, "message": `DeviceToken not found by the given key ${key}` };
102 | return;
103 | }
104 | if (!title) {
105 | title = getParamCompat(ctx, "title");
106 | }
107 | if (!body) {
108 | body = getParamCompat(ctx, "body");
109 | }
110 | let url = getParamCompat(ctx, "url");
111 | let copy = getParamCompat(ctx, "copy");
112 | let sound = getParamCompat(ctx, "sound", "1107");
113 | let isArchive = getParamCompat(ctx, "isArchive");
114 | let automaticallyCopy = getParamCompat(ctx, "automaticallyCopy");
115 | let category = "myNotificationCategory";
116 | let payload = {
117 | aps: {
118 | sound,
119 | category,
120 | "mutable-content": 1,
121 | alert: {
122 | title, body
123 | },
124 | },
125 | data: {
126 | isArchive, url, copy, automaticallyCopy
127 | },
128 | };
129 | let response = await client.send(new Notification(deviceToken, payload));
130 | console.log(response);
131 | ctx.body = { "code": 200, "message": response };
132 | }
133 |
134 | router.get("/ping", ping);
135 | router.get("/register", register);
136 | router.get("/:key/:body", send);
137 | router.get("/:key/:title/:body", send);
138 | router.post("/:key/:body", send);
139 | router.post("/:key/:title/:body", send);
140 |
141 | client.on(Errors.error, (err) => {
142 | console.error(JSON.stringify(err));
143 | });
144 |
145 | ensureInitDb();
146 |
147 | app.use(bodyParser());
148 | app.use(router.routes());
149 | app.listen(80);
150 |
--------------------------------------------------------------------------------
/bark-server/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "com.dss886.bark-server",
3 | "version": "1.0.0",
4 | "description": "Node.js implementation of the bark-server",
5 | "main": "index.js",
6 | "scripts": {
7 | "serve": "node index.js"
8 | },
9 | "author": "dss886",
10 | "license": "Apache License 2.0",
11 | "dependencies": {
12 | "@koa/router": "^10.0.0",
13 | "apns2": "^9.3.0",
14 | "koa": "^2.13.1",
15 | "koa-bodyparser": "^4.3.0",
16 | "sqlite3": "^5.0.1"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/ddddocr/README.md:
--------------------------------------------------------------------------------
1 | # ocr_api_server
2 | 使用ddddocr的最简api搭建项目
3 | **建议python版本3.7-3.9 64位**
4 |
--------------------------------------------------------------------------------
/ddddocr/main.py:
--------------------------------------------------------------------------------
1 | # encoding=utf-8
2 | import argparse
3 | import base64
4 | import json
5 |
6 | import ddddocr
7 | from flask import Flask, request
8 |
9 | parser = argparse.ArgumentParser(description="使用ddddocr搭建的最简api服务")
10 | parser.add_argument("-p", "--port", type=int, default=6147)
11 | parser.add_argument("--ocr", action="store_true", help="开启ocr识别")
12 | parser.add_argument("--old", action="store_true", help="OCR是否启动旧模型")
13 | parser.add_argument("--det", action="store_true", help="开启目标检测")
14 |
15 | args = parser.parse_args()
16 |
17 | app = Flask(__name__)
18 |
19 |
20 | class Server(object):
21 | def __init__(self, ocr=True, det=False, old=False):
22 | self.ocr_option = ocr
23 | self.det_option = det
24 | self.old_option = old
25 | self.ocr = None
26 | self.det = None
27 | if self.ocr_option:
28 | print("ocr模块开启")
29 | if self.old_option:
30 | print("使用OCR旧模型启动")
31 | self.ocr = ddddocr.DdddOcr(old=True)
32 | else:
33 | print("使用OCR新模型启动,如需要使用旧模型,请额外添加参数 --old开启")
34 | self.ocr = ddddocr.DdddOcr()
35 | else:
36 | print("ocr模块未开启,如需要使用,请使用参数 --ocr开启")
37 | if self.det_option:
38 | print("目标检测模块开启")
39 | self.det = ddddocr.DdddOcr(det=True)
40 | else:
41 | print("目标检测模块未开启,如需要使用,请使用参数 --det开启")
42 |
43 | def classification(self, img: bytes):
44 | if self.ocr_option:
45 | return self.ocr.classification(img)
46 | else:
47 | raise Exception("ocr模块未开启")
48 |
49 | def detection(self, img: bytes):
50 | if self.det_option:
51 | return self.det.detection(img)
52 | else:
53 | raise Exception("目标检测模块模块未开启")
54 |
55 | def slide(self, target_img: bytes, bg_img: bytes, algo_type: str):
56 | dddd = self.ocr or self.det or ddddocr.DdddOcr(ocr=False)
57 | if algo_type == 'match':
58 | return dddd.slide_match(target_img, bg_img)
59 | elif algo_type == 'compare':
60 | return dddd.slide_comparison(target_img, bg_img)
61 | else:
62 | raise Exception(f"不支持的滑块算法类型: {algo_type}")
63 |
64 | server = Server(ocr=args.ocr, det=args.det, old=args.old)
65 |
66 |
67 | def get_img(request, img_type='file', img_name='image'):
68 | if img_type == 'b64':
69 | img = base64.b64decode(request.get_data()) #
70 | try: # json str of multiple images
71 | dic = json.loads(img)
72 | img = base64.b64decode(dic.get(img_name).encode())
73 | except Exception as e: # just base64 of single image
74 | pass
75 | else:
76 | img = request.files.get(img_name).read()
77 | return img
78 |
79 |
80 | def set_ret(result, ret_type='text'):
81 | if ret_type == 'json':
82 | if isinstance(result, Exception):
83 | return json.dumps({"status": 200, "result": "", "msg": str(result)})
84 | else:
85 | return json.dumps({"status": 200, "result": result, "msg": ""})
86 | # return json.dumps({"succ": isinstance(result, str), "result": str(result)})
87 | else:
88 | if isinstance(result, Exception):
89 | return ''
90 | else:
91 | return str(result).strip()
92 |
93 |
94 | @app.route('//', methods=['POST'])
95 | @app.route('///', methods=['POST'])
96 | def ocr(opt, img_type='file', ret_type='text'):
97 | try:
98 | img = get_img(request, img_type)
99 | if opt == 'ocr':
100 | result = server.classification(img)
101 | elif opt == 'det':
102 | result = server.detection(img)
103 | else:
104 | raise f" is invalid"
105 | return set_ret(result, ret_type)
106 | except Exception as e:
107 | return set_ret(e, ret_type)
108 |
109 | @app.route('/slide//', methods=['POST'])
110 | @app.route('/slide///', methods=['POST'])
111 | def slide(algo_type='compare', img_type='file', ret_type='text'):
112 | try:
113 | target_img = get_img(request, img_type, 'target_img')
114 | bg_img = get_img(request, img_type, 'bg_img')
115 | result = server.slide(target_img, bg_img, algo_type)
116 | return set_ret(result, ret_type)
117 | except Exception as e:
118 | return set_ret(e, ret_type)
119 |
120 | @app.route('/ping', methods=['GET'])
121 | def ping():
122 | return "pong"
123 |
124 |
125 | if __name__ == '__main__':
126 | app.run(host="0.0.0.0", port=args.port)
127 |
--------------------------------------------------------------------------------
/ddddocr/requirements.txt:
--------------------------------------------------------------------------------
1 | ddddocr>=1.3.1
2 | flask
3 |
--------------------------------------------------------------------------------