Merge pull request #5 from chrisvrose/read-msgs
Finish implementing a way to receive messages
This commit is contained in:
10
README.md
10
README.md
@@ -2,7 +2,11 @@
|
|||||||
|
|
||||||
**indev** - still being made
|
**indev** - still being made
|
||||||
|
|
||||||
Telegram bot.
|
Telegram bot deployed using Heroku.
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
A test on async await, and typescript. Since this project is IO-bound(mostly), it makes sense to use Node and Typescript.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -20,6 +24,6 @@ JACK_TOKEN="u thought" npm start
|
|||||||
## Goals
|
## Goals
|
||||||
|
|
||||||
- [X] Read Name
|
- [X] Read Name
|
||||||
- [ ] Read msgs
|
- [X] Read msgs
|
||||||
- [ ] Deal with msgs
|
- [X] Deal with msgs
|
||||||
- [ ] Implement response bodies
|
- [ ] Implement response bodies
|
55
src/index.ts
55
src/index.ts
@@ -2,6 +2,8 @@ import delay from './misc/delay'
|
|||||||
import {teleargs,envVars,getUpdateBody} from './misc/defs'
|
import {teleargs,envVars,getUpdateBody} from './misc/defs'
|
||||||
import getName from './misc/getName'
|
import getName from './misc/getName'
|
||||||
import {getMessage,getInit} from './tg/getWrapper';
|
import {getMessage,getInit} from './tg/getWrapper';
|
||||||
|
import { replyStrategy} from './tg/replyStrategy';
|
||||||
|
import {sendWrapper} from './tg/sendWrapper'
|
||||||
import { assert } from 'console';
|
import { assert } from 'console';
|
||||||
|
|
||||||
const token = process.env['JACK_TOKEN'] as string;
|
const token = process.env['JACK_TOKEN'] as string;
|
||||||
@@ -10,15 +12,15 @@ if(token===undefined){
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (require.main === module) {
|
if (require.main === module) {
|
||||||
|
|
||||||
loop({token}).catch(reason => {
|
loop({token}).catch(reason => {
|
||||||
console.error("E:", reason);
|
console.error('E:', reason);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//Enable this to see cool viz
|
/**
|
||||||
// setInterval(()=>console.log("10secs"),10000);
|
* Main bot loop
|
||||||
|
* @param args Environment variables
|
||||||
|
*/
|
||||||
async function loop(args:envVars) {
|
async function loop(args:envVars) {
|
||||||
const requestURL = 'https://api.telegram.org/bot'+token;
|
const requestURL = 'https://api.telegram.org/bot'+token;
|
||||||
let name:string;
|
let name:string;
|
||||||
@@ -31,19 +33,41 @@ async function loop(args:envVars) {
|
|||||||
console.info(`Name: ${selfName}`);
|
console.info(`Name: ${selfName}`);
|
||||||
|
|
||||||
let lastUpdate = 0;
|
let lastUpdate = 0;
|
||||||
const timeout = 100;
|
|
||||||
|
//configurable parameters
|
||||||
|
const pollingInterval = 100;
|
||||||
|
const maxArraySize = 64;
|
||||||
|
const intervalTimerVal = 1000;
|
||||||
|
|
||||||
|
const sender = new sendWrapper(requestURL,intervalTimerVal/10,maxArraySize);
|
||||||
|
const requestArrays:Promise<boolean>[] = [];
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
//basically, check every second at max
|
//basically, check every second at max
|
||||||
let x = delay(1000);
|
let x = delay(intervalTimerVal);
|
||||||
|
|
||||||
//while we await, we can do other stuff
|
//while we await, we can do other stuff
|
||||||
lastUpdate = await main({name:selfName,token,requestURL,lastUpdate,timeout});
|
const responses= await main({name:selfName,token,requestURL,lastUpdate,timeout: pollingInterval});
|
||||||
|
//update the last response number
|
||||||
|
lastUpdate = responses.maxMsgUpdateID;
|
||||||
|
//push it all in
|
||||||
|
await sender.pushAll(responses.sendables);
|
||||||
|
|
||||||
|
requestArrays.push(sender.popMost());
|
||||||
|
|
||||||
|
|
||||||
|
//~~ makes it an integer
|
||||||
|
if(requestArrays.length>~~(maxArraySize/10)){
|
||||||
|
//ensure the request some time ago has completed.
|
||||||
|
//If it hasnt, it should stall everything
|
||||||
|
await requestArrays.shift();
|
||||||
|
}
|
||||||
|
|
||||||
await x;
|
await x;
|
||||||
}
|
}
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error("E:",e.msg||e.message||'Error');
|
console.error('E:',e.msg||e.message||'Error');
|
||||||
setTimeout(loop,timeout*101);
|
setTimeout(loop,pollingInterval*101);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,7 +77,7 @@ async function loop(args:envVars) {
|
|||||||
* @returns the last update we got
|
* @returns the last update we got
|
||||||
*/
|
*/
|
||||||
async function main(args:teleargs) {
|
async function main(args:teleargs) {
|
||||||
console.debug("Logging Update for:",args.lastUpdate);
|
// console.debug("Logging Update for:",args.lastUpdate);
|
||||||
const actions = await getMessage(args);
|
const actions = await getMessage(args);
|
||||||
const body = actions.body as unknown as getUpdateBody;
|
const body = actions.body as unknown as getUpdateBody;
|
||||||
assert(body.ok);
|
assert(body.ok);
|
||||||
@@ -61,14 +85,15 @@ async function main(args:teleargs) {
|
|||||||
|
|
||||||
//Do something
|
//Do something
|
||||||
|
|
||||||
console.debug(body.result);
|
// console.debug(body.result);
|
||||||
|
|
||||||
|
const sendables = await replyStrategy(body.result);
|
||||||
|
|
||||||
|
|
||||||
// const update_id = actions.body.
|
|
||||||
// return the update number
|
// return the update number
|
||||||
const getMaxMsgUpdateID:number = body.result.reduce((acc,curr)=>{
|
const maxMsgUpdateID:number = body.result.reduce((acc,curr)=>{
|
||||||
return curr.update_id>acc?curr.update_id:acc;
|
return curr.update_id>acc?curr.update_id:acc;
|
||||||
},args.lastUpdate);
|
},args.lastUpdate);
|
||||||
return getMaxMsgUpdateID;
|
return {maxMsgUpdateID,sendables};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -12,9 +12,24 @@ export interface envVars{
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface userObj{
|
||||||
|
id:number,
|
||||||
|
is_bot:boolean,
|
||||||
|
first_name:string,
|
||||||
|
last_name?:string,
|
||||||
|
username?:string
|
||||||
|
}
|
||||||
export interface messageObj{
|
export interface messageObj{
|
||||||
|
message_id:number,
|
||||||
|
from?:userObj,//TODOF add user
|
||||||
|
date:number,
|
||||||
|
chat:any,//TODOF add Chat
|
||||||
|
audio?:any,
|
||||||
|
document?:any,
|
||||||
|
photo?:any,
|
||||||
|
sticker?:any,
|
||||||
|
caption?:string,
|
||||||
|
text?:string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface getUpdateResultBody{
|
export interface getUpdateResultBody{
|
||||||
@@ -26,17 +41,14 @@ export interface getUpdateBody{
|
|||||||
result:getUpdateResultBody[]
|
result:getUpdateResultBody[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface sendMessage{
|
||||||
|
chat_id:number,
|
||||||
|
text:string,
|
||||||
|
disable_notification?:boolean,
|
||||||
|
reply_to_message_id?: number
|
||||||
|
}
|
||||||
|
|
||||||
export interface initBody{
|
export interface initBody{
|
||||||
ok:boolean,
|
ok:boolean,
|
||||||
result:{
|
result:userObj
|
||||||
id:number,
|
|
||||||
is_bot:boolean,
|
|
||||||
first_name:string,
|
|
||||||
username:string,
|
|
||||||
can_join_groups:boolean,
|
|
||||||
can_read_all_group_messages:boolean,
|
|
||||||
supports_inline_queries: boolean
|
|
||||||
}
|
|
||||||
}
|
}
|
48
src/tg/replyStrategy.ts
Normal file
48
src/tg/replyStrategy.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
|
||||||
|
import { getUpdateResultBody, sendMessage } from '../misc/defs';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the non-text out
|
||||||
|
* @param updates set of updates
|
||||||
|
*/
|
||||||
|
export async function replyStrategy(updates: getUpdateResultBody[]) {
|
||||||
|
const nonTextMessages = updates.filter(e => e.message && !e.message.text);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const textMessages = updates.filter(e => e.message && e.message.text);
|
||||||
|
|
||||||
|
//Log stats
|
||||||
|
if (textMessages.length > 0||nonTextMessages.length > 0) {
|
||||||
|
console.log(`Got: ${textMessages.length} text messages,${nonTextMessages.length} weird stuff`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const textReplies = await replyText(textMessages);
|
||||||
|
const nonTextReplies = await replyNonText(nonTextMessages);
|
||||||
|
|
||||||
|
return [...textReplies,...nonTextReplies];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export async function replyText(updates: getUpdateResultBody[]): Promise<sendMessage[]> {
|
||||||
|
return updates.map(e => {
|
||||||
|
return {
|
||||||
|
chat_id: e.message?.from?.id as number,
|
||||||
|
reply_to_message_id: e.message?.message_id,
|
||||||
|
text: 'Hello World!'
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function replyNonText(updates: getUpdateResultBody[]):Promise<sendMessage[]> {
|
||||||
|
return updates.map(e => {
|
||||||
|
return {
|
||||||
|
chat_id: e.message?.from?.id as number,
|
||||||
|
text: 'This is useless to me :(',
|
||||||
|
reply_to_message_id: e.message?.message_id
|
||||||
|
};
|
||||||
|
})
|
||||||
|
}
|
76
src/tg/sendWrapper.ts
Normal file
76
src/tg/sendWrapper.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { sendMessage } from '../misc/defs'
|
||||||
|
import got from 'got';
|
||||||
|
import assert from 'assert';
|
||||||
|
import delay from '../misc/delay'
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Buffers the output
|
||||||
|
*/
|
||||||
|
export class sendWrapper {
|
||||||
|
queue: sendMessage[];
|
||||||
|
maxLength: number;
|
||||||
|
sendURL: string;
|
||||||
|
minDelay: number;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param requestURL request URL to send via
|
||||||
|
* @param minDelay Minimum delay between
|
||||||
|
* @param maxLength Max length of allowed array
|
||||||
|
*/
|
||||||
|
constructor(requestURL: string, minDelay: number = 500, maxLength: number = 100) {
|
||||||
|
this.queue = [];
|
||||||
|
this.minDelay = minDelay;
|
||||||
|
this.sendURL = requestURL + '/sendMessage';
|
||||||
|
this.maxLength = maxLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param message Message to send
|
||||||
|
* @returns Whether push was successful
|
||||||
|
*/
|
||||||
|
public async push(message: sendMessage) {
|
||||||
|
if (this.queue.length < this.maxLength) {
|
||||||
|
this.queue.push(message);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.warn("Too many responses");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public async pushAll(messages:sendMessage[]){
|
||||||
|
if (this.queue.length+messages.length < this.maxLength) {
|
||||||
|
this.queue.push(...messages);
|
||||||
|
} else {
|
||||||
|
//best fit
|
||||||
|
this.queue.push(...messages.slice(0,this.maxLength-messages.length));
|
||||||
|
console.warn("Too many responses");
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send all required
|
||||||
|
*/
|
||||||
|
public async popMost(): Promise<boolean> {
|
||||||
|
while (this.queue.length > 0) {
|
||||||
|
//Removing this
|
||||||
|
const element = this.queue.shift()
|
||||||
|
if (element) {
|
||||||
|
const mintimer = delay(this.minDelay);
|
||||||
|
try {
|
||||||
|
const answ = await got.post(this.sendURL, { json: element });
|
||||||
|
}catch(e){
|
||||||
|
console.error("Failed to send:",element.text);
|
||||||
|
return false;
|
||||||
|
}finally{
|
||||||
|
await mintimer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user