Merge pull request #1 from iwestlin/master

机器人部署
This commit is contained in:
vitaminx 2020-06-29 22:02:21 +08:00 committed by GitHub
commit 52b04aad75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 203 additions and 15 deletions

BIN
doc/bot-worked.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 KiB

129
doc/tgbot-appache2-note.md Normal file
View File

@ -0,0 +1,129 @@
# 几个坑
* Telegram Bot API 提供了两种方式, webhook 和 long polling目前项目只支持 webhook 方式。
* webhook 方式必须要用HTTPS 也就是需要准备**个人域名**和**一个有效证书**
* 证书一定要单独域名证书(泛域名证书不能用)
# 原理/思路
TG创建bot要起一个服务支持BOT的功能 所以需要配置webhook 让tg 和服务器建立连接。webhook 需要有HTTPS的外网域名并且修改DNS指向你所配置的服务器IP这样就能保证TG的请求可以顺利到达并且验证BOT。
在服务器内部如果如果是单BOT 可以直接用nodje 配合 PM2 直接起服务,然后修改server.js端口号443。 如果服务器上有多个服务,那么就需要用反向代理,反代简单说就是一个服务+映射规则 (ngnix或者apache后者其他都可以) 侦听80或者443端口如果有指定的映射请求 就转发到内部映射的各个服务。
例如
```
aaa.domain.com <=> locahost:3001
bbb.domain.com <=> locahost:3002
domain.com/ccc <=> localhost:3003
```
# 步骤
1. 需要去tg 创建一个bot会得到token 和bot的tgurl
2. BOT服务
1. 服务器上clone 项目安装node, npm install
2. 如果需要配置多个BOT, clone不同目录, server.js里修改配置port和config.js
3. 安装PM2在每个bot目录下 PM2 start server.js
4. ``` pm2 status``` 确认服务跑起来了
1. 如果没起来, 查log文件见底部
5. curl 检查本地连接, curl 检查远端连接, not found 就对了
3. 外部连接
1. 修改DNS我是用cloudflare 把添加A record 直接把静态IP 绑定
2. 绑定以后, 本地开个terminal, ping 刚添加域名直到解析的IP是你绑定的这步确保连接上是畅通的
4. apache2开启SSL和反代
1. 复制证书到任意位置
2. 运行底部命令
3. /etc/apache2/sites-available 下找到默认的.conf或者自己建个conf也行
4. 修改底部配置信息
5. 保存重启 ```service apache2 restart```
5. 剩下的就是配置和检查webhook这里面也有不少坑在反代配置文件部分。。记不清了。。
6. 如果一切顺利 /help 会弹出目录
```
pm2 部分
tail -200 ~/.pm2/logs/server-error.log
tail -200 ~/.pm2/logs/server-out.log
curl "localhost:23333"
curl "domain:23333"
SSL+反代
sudo a2enmod ssl
sudo a2enmod proxy
sudo a2enmod proxy_balancer
sudo a2enmod proxy_http
/etc/apache2/sites-available/xxx.conf
<VirtualHost *:443>
SSLEngine on
SSLProtocol all
SSLCertificateFile {{CERT_DIR}}/{{domain.cer}}
SSLCertificateKeyFile {{CERT_DIR}}/{{domain.key}}
SSLCACertificateFile {{CERT_DIR}}/{{domain.ca.cer}}
ServerName {{domain}}
ProxyRequests Off
ProxyPreserveHost On
ProxyVia Full
<Proxy *>
Require all granted
</Proxy>
# 这里我用的是子目录映射方式。懒得再申请一个证书。。domain.com/ccc <=> localhost:3003
ProxyPass /{{bot1url}}/ http://127.0.0.1:23334/ # bot1
ProxyPassReverse /{{bot1url}}/ http://127.0.0.1:23334/ # bot1
ProxyPass /{{bot2url}}/ http://127.0.0.1:23333/ # bot2
ProxyPassReverse /{{bot2url}}/ http://127.0.0.1:23333/ # bot2
</VirtualHost>
something for verify and DEBUG
Apache command:
service apache2 restart
service apache2 stop
service apache2 status
service apache2 reload
tail -100 /var/log/apache2/error.log
验证一下SSL:
https://www.ssllabs.com/ssltest/analyze.html 确保Trusted和In trust store是绿的反正我这两个绿的就TG就能找到的到
SET webhook
curl -F "url=https://{{domain}}/{{bot1url}}/api/gdurl/tgbot" 'https://api.telegram.org/bot{{BOT_TOKEN}}/setWebhook'
delete webhook
curl -F "url=" https://api.telegram.org/bot{{BOT_TOKEN}}/setWebhook
check webhook
curl "https://api.telegram.org/bot{{BOT_TOKEN}}/getWebhookInfo"
```
![avatar](/doc/bot-worked.png)
# Reference Link
https://core.telegram.org/bots
https://core.telegram.org/bots/api
https://www.jianshu.com/p/ca804497afa0

View File

@ -2,10 +2,27 @@
> 不只是最快的 google drive 拷贝工具 [与其他工具的对比](./compare.md) > 不只是最快的 google drive 拷贝工具 [与其他工具的对比](./compare.md)
## 常见问题
项目发布一天以内作者至少收到100种无法配置成功的反馈。。忙得焦头烂额暂时没空做搭建过程的录屏。
下面是一些网友的踩坑心得,如果你配置的时候也不小心掉进坑里,可以进去找找有没有解决办法:
- [ikarosone 基于宝塔的搭建过程](https://www.ikarosone.top/archives/195.html)
- [@greathappyforest 踩的坑](doc/tgbot-appache2-note.md)
## 搭建过程
[https://drive.google.com/drive/folders/1Lu7Cwh9lIJkfqYDIaJrFpzi8Lgdxr4zT](https://drive.google.com/drive/folders/1Lu7Cwh9lIJkfqYDIaJrFpzi8Lgdxr4zT)
需要注意的地方:
- 视频中省略了一个比较重要的步骤就是**从本地上传service account授权文件到 sa 目录下**tg机器人的所有操作都是通过sa授权的所以你们别忘了。。
- 视频中**nginx的配置里server_name就是你的二级域名需要和cloudflare的设置一样**的mybbbottt我分开录的视频所以没做到一致。
- 还有省略的步骤就是注册域名和把域名托管到cloudflare了这一步网上太多资料了甚至也有免费注册一年域名的地方 https://www.freenom.com/ ),具体教程大家搜搜看吧。
## 功能简介 ## 功能简介
本工具目前支持以下功能: 本工具目前支持以下功能:
- 统计任意您拥有相关权限的下同不再赘述目录的文件信息且支持以各种形式html, table, json导出。 - 统计任意您拥有相关权限的下同不再赘述目录的文件信息且支持以各种形式html, table, json导出。
支持中断恢复且统计过的目录信息会记录在本地数据库文件中gdurl.sqlite 支持中断恢复,且统计过的目录(包括其所有子孙目录)信息会记录在本地数据库文件中gdurl.sqlite
请在本项目目录下命令行输入 `./count -h` 查看使用帮助 请在本项目目录下命令行输入 `./count -h` 查看使用帮助
- 拷贝任意目录所有文件到您指定目录,同样支持中断恢复。 - 拷贝任意目录所有文件到您指定目录,同样支持中断恢复。
@ -24,6 +41,8 @@
## 环境配置 ## 环境配置
本工具需要安装nodejs客户端安装请访问[https://nodejs.org/zh-cn/download/](https://nodejs.org/zh-cn/download/),服务器安装可参考[https://github.com/nodesource/distributions/blob/master/README.md#debinstall](https://github.com/nodesource/distributions/blob/master/README.md#debinstall) 本工具需要安装nodejs客户端安装请访问[https://nodejs.org/zh-cn/download/](https://nodejs.org/zh-cn/download/),服务器安装可参考[https://github.com/nodesource/distributions/blob/master/README.md#debinstall](https://github.com/nodesource/distributions/blob/master/README.md#debinstall)
建议选择v12版本的node以防接下来安装依赖出错。
如果你的网络环境无法正常访问谷歌服务,需要先在命令行进行一些配置:(如果可以正常访问则跳过此节) 如果你的网络环境无法正常访问谷歌服务,需要先在命令行进行一些配置:(如果可以正常访问则跳过此节)
``` ```
http_proxy="YOUR_PROXY_URL" && https_proxy=$http_proxy && HTTP_PROXY=$http_proxy && HTTPS_PROXY=$http_proxy http_proxy="YOUR_PROXY_URL" && https_proxy=$http_proxy && HTTP_PROXY=$http_proxy && HTTPS_PROXY=$http_proxy
@ -32,10 +51,13 @@ http_proxy="YOUR_PROXY_URL" && https_proxy=$http_proxy && HTTP_PROXY=$http_proxy
## 依赖安装 ## 依赖安装
- 命令行执行`git clone https://github.com/iwestlin/gd-utils && cd gd-utils` 克隆并切换到本项目文件夹下 - 命令行执行`git clone https://github.com/iwestlin/gd-utils && cd gd-utils` 克隆并切换到本项目文件夹下
- 执行 `npm i` 安装依赖,部分依赖可能需要代理环境才能下载,所以需要上一步的配置 - **执行 `npm install --unsafe-perm=true --allow-root` 安装依赖**,部分依赖可能需要代理环境才能下载,所以需要上一步的配置
如果在安装过程中发生报错请切换nodejs版本到v12再试。如果报错信息里有`Error: not found: make`之类的消息说明你的命令行环境缺少make命令可参考[这里](https://askubuntu.com/questions/192645/make-command-not-found)或直接google搜索`Make Command Not Found` 如果在安装过程中发生报错请切换nodejs版本到v12再试。如果报错信息里有`Error: not found: make`之类的消息说明你的命令行环境缺少make命令可参考[这里](https://askubuntu.com/questions/192645/make-command-not-found)或直接google搜索`Make Command Not Found`
如果报错信息里有 `better-sqlite3`,先执行 `npm config set unsafe-perm=true`
然后 `rm -rf node_module` 删掉依赖目录,最后再执行下`npm i`安装试试。
依赖安装完成后,项目文件夹下会多出个`node_modules`目录,请不要删除它,接下来进行下一步配置。 依赖安装完成后,项目文件夹下会多出个`node_modules`目录,请不要删除它,接下来进行下一步配置。
## Service Account 配置 ## Service Account 配置
@ -63,12 +85,29 @@ http_proxy="YOUR_PROXY_URL" && https_proxy=$http_proxy && HTTP_PROXY=$http_proxy
首先在 [https://core.telegram.org/bots#6-botfather](https://core.telegram.org/bots#6-botfather) 根据指示拿到 bot 的 token然后填入 config.js 中的 `tg_token` 变量。 首先在 [https://core.telegram.org/bots#6-botfather](https://core.telegram.org/bots#6-botfather) 根据指示拿到 bot 的 token然后填入 config.js 中的 `tg_token` 变量。
然后获取自己的 telegram username这个username不是显示的名称而是tg个人网址后面的那串字符比如我的tg个人网址是 `https://t.me/viegg` ,用户名就是 `viegg`获取用户名的目的是在代码里配置白名单只允许特定的用户调用机器人。将username填入 `config.js`里的配置,像这样:
`tg_whitelist: ['viegg']`,就代表只允许我自己使用这个机器人了。
如果想把机器人的使用权限分享给别的用户,只需要改成这样子: `tg_whitelist: ['viegg', '其他人的username']`
接下来需要将代码部署到服务器上。 接下来需要将代码部署到服务器上。
如果你一开始就是在服务器上配置的,可以直接执行`npm i pm2 -g` 如果你一开始就是在服务器上配置的,可以直接执行`npm i pm2 -g`
如果你之前是在本地操作的,请在服务器上同样重复一遍,配置好相关参数后,执行`npm i pm2 -g`安装进程守护程序pm2 如果你之前是在本地操作的,请在服务器上同样重复一遍,配置好相关参数后,执行`npm i pm2 -g`安装进程守护程序pm2
安装好pm2之后执行 `pm2 start server.js`,代码运行后会在服务器上监听`23333`端口接下来可通过nginx或其他工具起一个web服务示例nginx配置 安装好pm2之后执行 `pm2 start server.js`,代码运行后会在服务器上监听`23333`端口。
如果你启动程序后想看运行日志,执行 `pm2 logs`
查看 pm2 守护的进程列表,执行 `pm2 l`
停止运行中的进程,执行 `pm2 stop 对应的进程名称`
**如果你修改了代码中的配置,需要 `pm2 reload server` 才能生效**。
> 如果你不想用nginx可以将`server.js`中的`23333`改成`80`直接监听80端口可能需要root权限
接下来可通过nginx或其他工具起一个web服务示例nginx配置
``` ```
server { server {
listen 80; listen 80;
@ -84,6 +123,14 @@ server {
``` ```
配置好nginx后可以再套一层cloudflare具体教程请自行搜索。 配置好nginx后可以再套一层cloudflare具体教程请自行搜索。
检查网站是否部署成功可以命令行执行请将YOUR_WEBSITE_URL替换成你的网址
```
curl 'YOUR_WEBSITE_URL/api/gdurl/count?fid=124pjM5LggSuwI1n40bcD5tQ13wS0M6wg'
```
![](./static/count.png)
如果返回了这样的文件统计,说明部署成功了。
最后,在命令行执行(请将[YOUR_WEBSITE]和[YOUR_BOT_TOKEN]分别替换成你自己的网址和bot token 最后,在命令行执行(请将[YOUR_WEBSITE]和[YOUR_BOT_TOKEN]分别替换成你自己的网址和bot token
``` ```
curl -F "url=[YOUR_WEBSITE]/api/gdurl/tgbot" 'https://api.telegram.org/bot[YOUR_BOT_TOKEN]/setWebhook' curl -F "url=[YOUR_WEBSITE]/api/gdurl/tgbot" 'https://api.telegram.org/bot[YOUR_BOT_TOKEN]/setWebhook'
@ -112,6 +159,12 @@ const DEFAULT_TARGET = '' // 必填拷贝默认目的地ID如果不指定t
## 注意事项 ## 注意事项
程序的原理是调用了[google drive官方接口](https://developers.google.com/drive/api/v3/reference/files/list),递归获取目标文件夹下所有文件及其子文件夹信息,粗略来讲,某个目录下包含多少个文件夹,就至少需要这么多次请求才能统计完成。 程序的原理是调用了[google drive官方接口](https://developers.google.com/drive/api/v3/reference/files/list),递归获取目标文件夹下所有文件及其子文件夹信息,粗略来讲,某个目录下包含多少个文件夹,就至少需要这么多次请求才能统计完成。
如果你要统计的文件数非常多一百万以上请一定在命令行进行操作因为程序运行的时候会把文件信息保存在内存中文件数太多的话容易内存占用太多被nodejs干掉。可以像这样执行命令
```
node --max-old-space-size=4096 count folder-id -S
```
这样进程就能最大占用4G内存了。
目前尚不知道google是否会对接口做频率限制也不知道会不会影响google账号本身的安全。 目前尚不知道google是否会对接口做频率限制也不知道会不会影响google账号本身的安全。
**请勿滥用,后果自负** **请勿滥用,后果自负**

View File

@ -18,7 +18,7 @@ app.use(ctx => {
}) })
const PORT = 23333 const PORT = 23333
app.listen(PORT, '127.0.0.1', console.log('http://127.0.0.1:' + PORT)) app.listen(PORT, '0.0.0.0', console.log('http://127.0.0.1:' + PORT))
async function catcher (ctx, next) { async function catcher (ctx, next) {
try { try {

View File

@ -315,13 +315,12 @@ async function create_folder (name, parent, use_sa) {
mimeType: FOLDER_TYPE, mimeType: FOLDER_TYPE,
parents: [parent] parents: [parent]
} }
const headers = await gen_headers(use_sa)
const config = { headers }
let retry = 0 let retry = 0
let data let data
while (!data && (retry < RETRY_LIMIT)) { while (!data && (retry < RETRY_LIMIT)) {
try { try {
data = (await axins.post(url, post_data, config)).data const headers = await gen_headers(use_sa)
data = (await axins.post(url, post_data, { headers })).data
} catch (err) { } catch (err) {
retry++ retry++
handle_error(err) handle_error(err)
@ -542,18 +541,23 @@ async function create_folders ({ source, old_mapping, folders, root, task_id, se
console.log('================') console.log('================')
console.log('已创建的文件夹数量', count) console.log('已创建的文件夹数量', count)
console.log('正在进行的网络请求', limit.activeCount) console.log('正在进行的网络请求', limit.activeCount)
console.log('排队等候的目录数量', limit.pendingCount) console.log('排队等候的网络请求', limit.pendingCount)
}, LOG_DELAY) }, LOG_DELAY)
while (same_levels.length) { while (same_levels.length) {
await Promise.all(same_levels.map(async v => { await Promise.all(same_levels.map(async v => {
try {
const { name, id, parent } = v const { name, id, parent } = v
const target = mapping[parent] || root const target = mapping[parent] || root
const new_folder = await limit(() => create_folder(name, target, service_account)) const new_folder = await limit(() => create_folder(name, target, service_account))
if (!new_folder) throw new Error(name + '创建失败')
count++ count++
mapping[id] = new_folder.id mapping[id] = new_folder.id
const mapping_record = id + ' ' + new_folder.id + '\n' const mapping_record = id + ' ' + new_folder.id + '\n'
db.prepare('update task set status=?, mapping = mapping || ? where id=?').run('copying', mapping_record, task_id) db.prepare('update task set status=?, mapping = mapping || ? where id=?').run('copying', mapping_record, task_id)
} catch (e) {
console.error('创建目录出错:', v, e)
}
})) }))
folders = folders.filter(v => !mapping[v.id]) folders = folders.filter(v => !mapping[v.id])
same_levels = [].concat(...same_levels.map(v => folders.filter(vv => vv.parent === v.id))) same_levels = [].concat(...same_levels.map(v => folders.filter(vv => vv.parent === v.id)))

View File

@ -90,6 +90,7 @@ router.post('/api/gdurl/tgbot', async ctx => {
} }
} else if (text.startsWith('/copy')) { } else if (text.startsWith('/copy')) {
const target = text.replace('/copy', '').trim().split(' ').map(v => v.trim())[1] const target = text.replace('/copy', '').trim().split(' ').map(v => v.trim())[1]
if (target && !validate_fid(target)) return sm({ chat_id, text: `目标ID ${target} 格式不正确` })
tg_copy({ fid, target, chat_id }).then(task_id => { tg_copy({ fid, target, chat_id }).then(task_id => {
task_id && sm({ chat_id, text: `开始复制任务ID: ${task_id} 可输入 /task ${task_id} 查询进度` }) task_id && sm({ chat_id, text: `开始复制任务ID: ${task_id} 可输入 /task ${task_id} 查询进度` })
}) })

View File

@ -152,6 +152,7 @@ async function send_count ({ fid, chat_id }) {
return axins.post(url, { return axins.post(url, {
chat_id, chat_id,
parse_mode: 'HTML', parse_mode: 'HTML',
// todo 输出文件名
text: `<pre>${gd_link} text: `<pre>${gd_link}
${table}</pre>` ${table}</pre>`
}).catch(async err => { }).catch(async err => {

BIN
static/count.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB