basic functions
This commit is contained in:
parent
2c4443bb26
commit
fe8272e0c5
@ -1,13 +0,0 @@
|
||||
# http://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
indent_style = tab
|
||||
tab_width = 2
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.yml]
|
||||
indent_style = space
|
5
.idea/.gitignore
vendored
Normal file
5
.idea/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
65
.idea/codeStyles/Project.xml
Normal file
65
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,65 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<code_scheme name="Project" version="173">
|
||||
<HTMLCodeStyleSettings>
|
||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||
<option name="HTML_QUOTE_STYLE" value="Single" />
|
||||
<option name="HTML_ENFORCE_QUOTES" value="true" />
|
||||
</HTMLCodeStyleSettings>
|
||||
<JSCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</JSCodeStyleSettings>
|
||||
<TypeScriptCodeStyleSettings version="0">
|
||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||
<option name="USE_DOUBLE_QUOTES" value="false" />
|
||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||
</TypeScriptCodeStyleSettings>
|
||||
<VueCodeStyleSettings>
|
||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||
</VueCodeStyleSettings>
|
||||
<codeStyleSettings language="HTML">
|
||||
<option name="SOFT_MARGINS" value="140" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JavaScript">
|
||||
<option name="SOFT_MARGINS" value="140" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="TypeScript">
|
||||
<option name="SOFT_MARGINS" value="140" />
|
||||
<indentOptions>
|
||||
<option name="INDENT_SIZE" value="2" />
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="TAB_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="Vue">
|
||||
<option name="SOFT_MARGINS" value="140" />
|
||||
<indentOptions>
|
||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||
</state>
|
||||
</component>
|
13
.idea/forward-air.iml
Normal file
13
.idea/forward-air.iml
Normal file
@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/temp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/tmp" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.wrangler" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
12
.idea/inspectionProfiles/Project_Default.xml
Normal file
12
.idea/inspectionProfiles/Project_Default.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="IncorrectHttpHeaderInspection" enabled="true" level="WARNING" enabled_by_default="true">
|
||||
<option name="customHeaders">
|
||||
<set>
|
||||
<option value="X-Auth-Token" />
|
||||
</set>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/forward-air.iml" filepath="$PROJECT_DIR$/.idea/forward-air.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
7
.idea/prettier.xml
Normal file
7
.idea/prettier.xml
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="PrettierConfiguration">
|
||||
<option name="myConfigurationMode" value="AUTOMATIC" />
|
||||
<option name="myRunOnSave" value="true" />
|
||||
</component>
|
||||
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,6 +0,0 @@
|
||||
{
|
||||
"printWidth": 140,
|
||||
"singleQuote": true,
|
||||
"semi": true,
|
||||
"useTabs": true
|
||||
}
|
8
src/.prettierrc
Normal file
8
src/.prettierrc
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"endOfLine": "lf",
|
||||
"printWidth": 140,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "es5"
|
||||
}
|
42
src/func/hook.ts
Normal file
42
src/func/hook.ts
Normal file
@ -0,0 +1,42 @@
|
||||
import { Bot, Message } from '../lib/bot';
|
||||
import { Env } from '../index';
|
||||
import { IsValidUUID } from '../lib/uuid';
|
||||
import { Status } from '../lib/status';
|
||||
import { Wrap } from '../lib/wrapper';
|
||||
|
||||
export async function FuncHook(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
if (request.method !== 'POST') return Wrap(Status.BadRequest, 'Only POST method is allowed');
|
||||
|
||||
const token = request.headers.get('X-Telegram-Bot-Api-Secret-Token');
|
||||
if (token !== env.TG_HOOK_TOKEN) return Wrap(Status.BadRequest, 'Invalid Token');
|
||||
|
||||
const message = ((await request.json()) as Message).message;
|
||||
if (message.from.is_bot || message.chat.type !== 'private') return Wrap(Status.OK, 'Only private from user is allowed');
|
||||
|
||||
const chat_id = message.chat.id;
|
||||
const text = message.text;
|
||||
|
||||
console.debug(`[Hook] ${chat_id} ${text}`);
|
||||
|
||||
if (text.startsWith('BIND:')) {
|
||||
const uuid = text.split(':')[1];
|
||||
if (!IsValidUUID(uuid)) return Wrap(Status.BadRequest, 'Invalid UUID');
|
||||
await env.KV.put(uuid, chat_id.toString());
|
||||
await new Bot(env.TG_BOT_TOKEN).send(chat_id, '[BOT] Bind Success');
|
||||
return Wrap(Status.OK, 'Bind successfully');
|
||||
}
|
||||
|
||||
if (text.startsWith('UNBIND:')) {
|
||||
const uuid = text.split(':')[1];
|
||||
if (!IsValidUUID(uuid)) return Wrap(Status.BadRequest, 'Invalid UUID');
|
||||
await env.KV.delete(uuid);
|
||||
await new Bot(env.TG_BOT_TOKEN).send(chat_id, '[BOT] Unbind Success');
|
||||
return Wrap(Status.OK, 'Unbind successfully');
|
||||
}
|
||||
|
||||
// TODO: Handle "SEND:" command
|
||||
|
||||
// TODO: Handle Cited Message (Text or Voice) with command
|
||||
|
||||
return Wrap(Status.OK, 'Nothing Executed');
|
||||
}
|
24
src/func/push.ts
Normal file
24
src/func/push.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Bot } from '../lib/bot';
|
||||
import { Env } from '../index';
|
||||
import { IsValidUUID } from '../lib/uuid';
|
||||
import { Status } from '../lib/status';
|
||||
import { Wrap } from '../lib/wrapper';
|
||||
|
||||
export async function FuncPush(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
if (request.method !== 'POST') return Wrap(Status.BadRequest, 'Only POST method is allowed');
|
||||
|
||||
const token = request.headers.get('X-Auth-Token');
|
||||
if (!token) return Wrap(Status.BadRequest, 'Missing X-Auth-Token header');
|
||||
if (!IsValidUUID(token)) return Wrap(Status.BadRequest, 'Invalid Token');
|
||||
|
||||
const chat_id = await env.KV.get(token);
|
||||
if (!chat_id) return Wrap(Status.BadRequest, 'Invalid Token');
|
||||
|
||||
const bot = new Bot(env.TG_BOT_TOKEN);
|
||||
const message = await request.text();
|
||||
await bot.send(+chat_id, message);
|
||||
|
||||
console.debug(`[Push] ${chat_id} ${message}`);
|
||||
|
||||
return Wrap(Status.OK, null);
|
||||
}
|
51
src/index.ts
51
src/index.ts
@ -1,32 +1,29 @@
|
||||
/**
|
||||
* Welcome to Cloudflare Workers! This is your first worker.
|
||||
*
|
||||
* - Run `npm run dev` in your terminal to start a development server
|
||||
* - Open a browser tab at http://localhost:8787/ to see your worker in action
|
||||
* - Run `npm run deploy` to publish your worker
|
||||
*
|
||||
* Learn more at https://developers.cloudflare.com/workers/
|
||||
*/
|
||||
import { NotFound } from './lib/wrapper';
|
||||
import { FuncPush } from './func/push';
|
||||
import { FuncHook } from './func/hook';
|
||||
|
||||
export interface Env {
|
||||
// Example binding to KV. Learn more at https://developers.cloudflare.com/workers/runtime-apis/kv/
|
||||
// MY_KV_NAMESPACE: KVNamespace;
|
||||
//
|
||||
// Example binding to Durable Object. Learn more at https://developers.cloudflare.com/workers/runtime-apis/durable-objects/
|
||||
// MY_DURABLE_OBJECT: DurableObjectNamespace;
|
||||
//
|
||||
// Example binding to R2. Learn more at https://developers.cloudflare.com/workers/runtime-apis/r2/
|
||||
// MY_BUCKET: R2Bucket;
|
||||
//
|
||||
// Example binding to a Service. Learn more at https://developers.cloudflare.com/workers/runtime-apis/service-bindings/
|
||||
// MY_SERVICE: Fetcher;
|
||||
//
|
||||
// Example binding to a Queue. Learn more at https://developers.cloudflare.com/queues/javascript-apis/
|
||||
// MY_QUEUE: Queue;
|
||||
KV: KVNamespace;
|
||||
BUCKET: R2Bucket;
|
||||
TG_BOT_TOKEN: string;
|
||||
TG_HOOK_TOKEN: string;
|
||||
}
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
return new Response('Hello World!');
|
||||
},
|
||||
export type Handler = (request: Request, env: Env, ctx: ExecutionContext) => Promise<Response>;
|
||||
|
||||
const routes: { [page: string]: Handler } = {
|
||||
'/push': FuncPush,
|
||||
'/hook': FuncHook,
|
||||
};
|
||||
|
||||
export default {
|
||||
async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
|
||||
const { pathname } = new URL(request.url);
|
||||
for (const route in routes) {
|
||||
if (pathname === route) {
|
||||
return routes[route](request, env, ctx);
|
||||
}
|
||||
}
|
||||
return NotFound();
|
||||
},
|
||||
};
|
||||
|
69
src/lib/bot.ts
Normal file
69
src/lib/bot.ts
Normal file
@ -0,0 +1,69 @@
|
||||
// import { console } from '@cloudflare/workers-types';
|
||||
|
||||
export interface Message {
|
||||
update_id: number;
|
||||
message: {
|
||||
message_id: number;
|
||||
from: {
|
||||
id: number;
|
||||
is_bot: boolean;
|
||||
username: string;
|
||||
};
|
||||
chat: {
|
||||
id: number;
|
||||
username: string;
|
||||
type: string;
|
||||
};
|
||||
date: number;
|
||||
text: string;
|
||||
};
|
||||
}
|
||||
|
||||
export class Bot {
|
||||
readonly token: string = '';
|
||||
|
||||
constructor(token: string) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
async fetch(): Promise<Message[]> {
|
||||
const url = `https://api.telegram.org/bot${this.token}/getUpdates`;
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
console.debug(`Fetch message response: ${response.status} ${response.body}`);
|
||||
|
||||
const body = (await response.json()) as { ok: boolean; result: Message[] };
|
||||
|
||||
if (response.ok && body.ok) return body.result;
|
||||
else return Promise.reject(`Fetch message failed`);
|
||||
}
|
||||
|
||||
async send(chat_id: number, message: string): Promise<void> {
|
||||
console.log(`Message sent to ${chat_id}: ${message}`);
|
||||
|
||||
const url = `https://api.telegram.org/bot${this.token}/sendMessage`;
|
||||
const body = {
|
||||
chat_id: chat_id,
|
||||
text: message,
|
||||
};
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
console.debug(`Send message response: ${response.status} ${response.body}`);
|
||||
|
||||
if (response.ok) return Promise.resolve();
|
||||
else return Promise.reject(`Send message failed`);
|
||||
}
|
||||
}
|
6
src/lib/status.ts
Normal file
6
src/lib/status.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const enum Status {
|
||||
OK = 200,
|
||||
BadRequest = 400,
|
||||
NotFound = 404,
|
||||
InternalServerError = 500,
|
||||
}
|
9
src/lib/uuid.ts
Normal file
9
src/lib/uuid.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export function GenUUID(): string {
|
||||
return '10000000-1000-4000-8000-100000000000'.replace(/[018]/g, (c) =>
|
||||
(c ^ (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))).toString(16)
|
||||
);
|
||||
}
|
||||
|
||||
export function IsValidUUID(uuid: string): boolean {
|
||||
return uuid.match(/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i) !== null;
|
||||
}
|
20
src/lib/wrapper.ts
Normal file
20
src/lib/wrapper.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { Status } from './status';
|
||||
|
||||
export async function Wrap(status: Status, body: any): Promise<Response> {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: status,
|
||||
body: status == Status.OK ? body : null,
|
||||
message: status == Status.OK ? null : body,
|
||||
}),
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export async function NotFound(): Promise<Response> {
|
||||
return Wrap(Status.NotFound, 'Not Found');
|
||||
}
|
@ -10,15 +10,15 @@ compatibility_date = "2023-11-21"
|
||||
|
||||
# Bind a KV Namespace. Use KV as persistent storage for small key-value pairs.
|
||||
# Docs: https://developers.cloudflare.com/workers/runtime-apis/kv
|
||||
# [[kv_namespaces]]
|
||||
# binding = "MY_KV_NAMESPACE"
|
||||
# id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
[[kv_namespaces]]
|
||||
binding = "KV"
|
||||
id = "b1d40d195c9b4bc8ab53d94e15564faa"
|
||||
|
||||
# Bind an R2 Bucket. Use R2 to store arbitrarily large blobs of data, such as files.
|
||||
# Docs: https://developers.cloudflare.com/r2/api/workers/workers-api-usage/
|
||||
# [[r2_buckets]]
|
||||
# binding = "MY_BUCKET"
|
||||
# bucket_name = "my-bucket"
|
||||
[[r2_buckets]]
|
||||
binding = "BUCKET"
|
||||
bucket_name = "forward-air"
|
||||
|
||||
# Bind a Queue producer. Use this binding to schedule an arbitrary task that may be processed later by a Queue consumer.
|
||||
# Docs: https://developers.cloudflare.com/queues/get-started
|
||||
|
Loading…
Reference in New Issue
Block a user