mirror of
https://codeberg.org/keyoxide/doipjs.git
synced 2024-12-22 14:39:28 -07:00
Merge pull request 'Implement Telegram support' (#24) from Goldstein/doipjs:telegram into main
Reviewed-on: https://codeberg.org/keyoxide/doipjs/pulls/24
This commit is contained in:
commit
c0cf0f2767
6 changed files with 224 additions and 0 deletions
|
@ -18,6 +18,7 @@ const list = [
|
|||
'irc',
|
||||
'xmpp',
|
||||
'matrix',
|
||||
'telegram',
|
||||
'twitter',
|
||||
'reddit',
|
||||
'liberapay',
|
||||
|
@ -39,6 +40,7 @@ const data = {
|
|||
irc: require('./irc'),
|
||||
xmpp: require('./xmpp'),
|
||||
matrix: require('./matrix'),
|
||||
telegram: require('./telegram'),
|
||||
twitter: require('./twitter'),
|
||||
reddit: require('./reddit'),
|
||||
liberapay: require('./liberapay'),
|
||||
|
|
82
src/claimDefinitions/telegram.js
Normal file
82
src/claimDefinitions/telegram.js
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
Copyright 2022 Maximilian Siling
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const E = require('../enums')
|
||||
|
||||
const reURI = /https:\/\/t.me\/([A-Za-z0-9_]{5,32})\?proof=([A-Za-z0-9_]{5,32})/
|
||||
|
||||
const processURI = (uri) => {
|
||||
const match = uri.match(reURI)
|
||||
|
||||
return {
|
||||
serviceprovider: {
|
||||
type: 'communication',
|
||||
name: 'telegram'
|
||||
},
|
||||
match: {
|
||||
regularExpression: reURI,
|
||||
isAmbiguous: false
|
||||
},
|
||||
profile: {
|
||||
display: `@${match[1]}`,
|
||||
uri: `https://t.me/${match[1]}`,
|
||||
qr: `https://t.me/${match[1]}`
|
||||
},
|
||||
proof: {
|
||||
uri: `https://t.me/${match[2]}`,
|
||||
request: {
|
||||
fetcher: E.Fetcher.TELEGRAM,
|
||||
access: E.ProofAccess.GRANTED,
|
||||
format: E.ProofFormat.JSON,
|
||||
data: {
|
||||
user: match[1],
|
||||
chat: match[2]
|
||||
}
|
||||
}
|
||||
},
|
||||
claim: {
|
||||
format: E.ClaimFormat.URI,
|
||||
relation: E.ClaimRelation.EQUALS,
|
||||
path: ['text']
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const tests = [
|
||||
{
|
||||
uri: 'https://t.me/alice?proof=foobar',
|
||||
shouldMatch: true
|
||||
},
|
||||
{
|
||||
uri: 'https://t.me/complex_user_1234?proof=complex_chat_1234',
|
||||
shouldMatch: true
|
||||
},
|
||||
{
|
||||
uri: 'https://t.me/foobar',
|
||||
shouldMatch: false
|
||||
},
|
||||
{
|
||||
uri: 'https://t.me/foobar?proof=',
|
||||
shouldMatch: false
|
||||
},
|
||||
{
|
||||
uri: 'https://t.me/?proof=foobar',
|
||||
shouldMatch: false
|
||||
}
|
||||
]
|
||||
|
||||
exports.reURI = reURI
|
||||
exports.processURI = processURI
|
||||
exports.tests = tests
|
|
@ -49,6 +49,8 @@ const Fetcher = {
|
|||
XMPP: 'xmpp',
|
||||
/** HTTP request to Matrix API */
|
||||
MATRIX: 'matrix',
|
||||
/** HTTP request to Telegram API */
|
||||
TELEGRAM: 'telegram',
|
||||
/** HTTP request to Twitter API */
|
||||
TWITTER: 'twitter'
|
||||
}
|
||||
|
|
|
@ -18,5 +18,6 @@ exports.dns = require('./dns')
|
|||
exports.http = require('./http')
|
||||
exports.irc = require('./irc')
|
||||
exports.matrix = require('./matrix')
|
||||
exports.telegram = require('./telegram')
|
||||
exports.twitter = require('./twitter')
|
||||
exports.xmpp = require('./xmpp')
|
||||
|
|
110
src/fetcher/telegram.js
Normal file
110
src/fetcher/telegram.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright 2022 Maximilian Siling
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
const axios = require('axios')
|
||||
const validator = require('validator')
|
||||
|
||||
/**
|
||||
* @module fetcher/telegram
|
||||
*/
|
||||
|
||||
/**
|
||||
* The single request's timeout value in milliseconds
|
||||
* This fetcher makes two requests in total
|
||||
* @constant {number} timeout
|
||||
*/
|
||||
module.exports.timeout = 5000
|
||||
|
||||
/**
|
||||
* Execute a fetch request
|
||||
* @function
|
||||
* @async
|
||||
* @param {object} data - Data used in the request
|
||||
* @param {string} data.chat - Telegram public chat username
|
||||
* @param {string} data.user - Telegram user username
|
||||
* @param {object} opts - Options used to enable the request
|
||||
* @param {string} opts.claims.telegram.token - The Telegram Bot API token
|
||||
* @returns {object|string}
|
||||
*/
|
||||
module.exports.fn = async (data, opts) => {
|
||||
let timeoutHandle
|
||||
const timeoutPromise = new Promise((resolve, reject) => {
|
||||
timeoutHandle = setTimeout(
|
||||
() => reject(new Error('Request was timed out')),
|
||||
data.fetcherTimeout ? data.fetcherTimeout : module.exports.timeout
|
||||
)
|
||||
})
|
||||
|
||||
const apiPromise = (method) => new Promise((resolve, reject) => {
|
||||
try {
|
||||
validator.isAscii(opts.claims.telegram.token)
|
||||
} catch (err) {
|
||||
throw new Error(`Telegram fetcher was not set up properly (${err.message})`)
|
||||
}
|
||||
|
||||
if (!data.chat || !data.user) {
|
||||
reject(new Error('Both chat name and user name must be provided'))
|
||||
return
|
||||
}
|
||||
|
||||
const url = `https://api.telegram.org/bot${opts.claims.telegram.token}/${method}?chat_id=@${data.chat}`
|
||||
axios.get(url, {
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'User-Agent': `doipjs/${require('../../package.json').version}`
|
||||
},
|
||||
validateStatus: (status) => status === 200
|
||||
})
|
||||
.then(res => resolve(res.data))
|
||||
.catch(e => reject(e))
|
||||
})
|
||||
|
||||
const fetchPromise = apiPromise('getChatAdministrators').then(admins => {
|
||||
if (!admins.ok) {
|
||||
throw new Error('Request to get chat administrators failed')
|
||||
}
|
||||
|
||||
return apiPromise('getChat').then(chat => {
|
||||
if (!chat.ok) {
|
||||
throw new Error('Request to get chat info failed')
|
||||
}
|
||||
|
||||
let creator
|
||||
for (const admin of admins.result) {
|
||||
if (admin.status === 'creator') {
|
||||
creator = admin.user.username
|
||||
}
|
||||
}
|
||||
|
||||
if (!chat.result.description) {
|
||||
throw new Error('There is no chat description')
|
||||
}
|
||||
|
||||
if (creator !== data.user) {
|
||||
throw new Error('User doesn\'t match')
|
||||
}
|
||||
|
||||
return {
|
||||
user: creator,
|
||||
text: chat.result.description
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
return Promise.race([fetchPromise, timeoutPromise]).then((result) => {
|
||||
clearTimeout(timeoutHandle)
|
||||
return result
|
||||
})
|
||||
}
|
|
@ -28,6 +28,9 @@ const opts = {
|
|||
instance: process.env.MATRIX_INSTANCE || null,
|
||||
accessToken: process.env.MATRIX_ACCESS_TOKEN || null
|
||||
},
|
||||
telegram: {
|
||||
token: process.env.TELEGRAM_TOKEN || null
|
||||
},
|
||||
xmpp: {
|
||||
service: process.env.XMPP_SERVICE || null,
|
||||
username: process.env.XMPP_USERNAME || null,
|
||||
|
@ -172,6 +175,30 @@ router.get(
|
|||
}
|
||||
)
|
||||
|
||||
// Telegram route
|
||||
router.get(
|
||||
'/get/telegram', query('chat').isString(),
|
||||
async (req, res) => {
|
||||
if (!opts.claims.telegram.token) {
|
||||
return res.status(501).json({ errors: 'Telegram not enabled on server' })
|
||||
}
|
||||
|
||||
const errors = validationResult(req)
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() })
|
||||
}
|
||||
|
||||
fetcher.telegram
|
||||
.fn(req.query, opts)
|
||||
.then((data) => {
|
||||
return res.status(200).send(data)
|
||||
})
|
||||
.catch((err) => {
|
||||
return res.status(400).json({ errors: err.message ? err.message : err })
|
||||
})
|
||||
}
|
||||
)
|
||||
|
||||
// IRC route
|
||||
router.get('/get/irc', query('nick').isString(), async (req, res) => {
|
||||
if (!opts.claims.irc.nick) {
|
||||
|
|
Loading…
Reference in a new issue