基于Flask的Telegram Bot对接VirusTotal轮询逻辑失效求助
问题描述
我开发了一个基于Flask的Telegram Bot宠物项目,功能是接收用户发送的文件或哈希值,提交到VirusTotal进行检测,并返回详细的检测报告。但发送VirusTotal从未检测过的文件时,Bot会返回An error occurred while checking the file on VirusTotal错误。我推测是VirusTotal还没完成检测就收到了GET请求,于是添加了response_code=0时等待10秒并重发请求的循环逻辑,但问题仍未解决。作为Python新手,恳请帮忙排查问题。
现有代码
# Import required modules import requests import hashlib import time from flask import Flask, request # Set bot token and VirusTotal API key BOT_TOKEN = '' # define the VirusTotal API key VT_API_KEY = '' # Create Flask app app = Flask(__name__) # Function to handle incoming messages def handle_message(message): chat_id = message['chat']['id'] # Check if message contains a document if 'document' in message: file_id = message['document']['file_id'] file_url = f'https://api.telegram.org/bot{BOT_TOKEN}/getFile?file_id={file_id}' response = requests.get(file_url) if not response.ok: send_message(chat_id, 'An error occurred while downloading the file') return file_path = response.json().get('result', {}).get('file_path') if not file_path: send_message(chat_id, 'An error occurred while retrieving the file path') return file_url = f'https://api.telegram.org/file/bot{BOT_TOKEN}/{file_path}' # Download file content response = requests.get(file_url) if not response.ok: send_message(chat_id, 'An error occurred while downloading the file content') return content = response.content # Compute file hash file_hash = hashlib.sha256(content).hexdigest() # 保存文件内容,用于后续上传(如果需要) downloaded_file = content # If message doesn't contain a document, assume it contains a hash else: file_hash = message['text'] downloaded_file = None # If hash is not of length 32 or 64, compute hash of hash value if len(file_hash) not in (32, 64): file_hash = hashlib.sha256(file_hash.encode()).hexdigest() # Build VirusTotal URL for file report vt_report_url = f'https://www.virustotal.com/vtapi/v2/file/report?apikey={VT_API_KEY}&resource={file_hash}' # Query VirusTotal for file report vt_response = requests.get(vt_report_url).json() # 处理资源不存在的情况(response_code=0) if vt_response.get('response_code') == 0: # 如果是用户上传的文件,尝试上传到VirusTotal if downloaded_file is not None: vt_upload_url = 'https://www.virustotal.com/vtapi/v2/file/scan' upload_data = {'apikey': VT_API_KEY} upload_files = {'file': ('uploaded_file', downloaded_file)} upload_response = requests.post(vt_upload_url, data=upload_data, files=upload_files).json() # 检查上传是否成功 if upload_response.get('response_code') == 1: scan_id = upload_response.get('scan_id') # 循环等待检测完成 while True: vt_response = requests.get(f'https://www.virustotal.com/vtapi/v2/file/report?apikey={VT_API_KEY}&resource={scan_id}').json() if vt_response.get('response_code') == 1: break # 免费版API有请求频率限制,不要太频繁 time.sleep(15) else: send_message(chat_id, 'Failed to upload file to VirusTotal') return else: # 如果是哈希且不存在,提示用户 send_message(chat_id, 'This hash is not found in VirusTotal database') return # Check if file report was retrieved successfully if vt_response.get('response_code') == 1: positives = vt_response.get('positives') total = vt_response.get('total') permalink = vt_response.get('permalink') # If file is clean, send response indicating clean status and include detailed report URL if positives == 0: response_text = f'The file has been checked on VirusTotal and is clean ({total} scans). Here is the detailed report: {permalink}' # If file is malicious, send response indicating malicious status and include detailed report URL else: response_text = f'The file has been checked on VirusTotal and is malicious ({positives}/{total} detections). Here is the detailed report: {permalink}' # If file report couldn't be retrieved, send error response else: response_text = 'An error occurred while checking the file on VirusTotal' # Send response message to user send_message(chat_id, response_text) # Function to send message to Telegram def send_message(chat_id, text): url = f'https://api.telegram.org/bot{BOT_TOKEN}/sendMessage?chat_id={chat_id}&text={text}' requests.post(url) # define a route to receive incoming Telegram messages @app.route('/', methods=['POST']) def receive_message(): message = request.json['message'] if 'document' in message or ('text' in message and len(message['text']) in (32, 64)): handle_message(message) return 'OK' # run the Flask app if __name__ == '__main__': app.run(host="0.0.0.0", port=5000)
关键修改点说明
- 核心逻辑修正:VirusTotal返回
response_code=0不是检测未完成,而是该资源在数据库中不存在。此时如果是用户上传的文件,需要先调用文件上传API(/file/scan)将文件提交到VT,再用返回的scan_id轮询报告。 - 保存文件内容:在下载文件后保存
downloaded_file变量,用于后续上传操作。 - 上传处理:添加文件上传逻辑,上传成功后用
scan_id循环查询报告,直到response_code=1(报告就绪)。 - 哈希不存在的提示:如果用户输入的哈希在VT数据库中不存在,直接提示用户,避免无效循环。
- 请求频率控制:将等待时间调整为15秒,适配VirusTotal免费版API的请求限制(每分钟最多4次请求)。
额外注意事项
- 确保
BOT_TOKEN和VT_API_KEY已正确填写,VirusTotal免费版API有请求次数限制,超过会返回429错误,可添加错误处理逻辑重试。 - 大文件上传可能需要更长时间,可根据实际情况调整等待间隔。
内容的提问来源于stack exchange,提问作者shamil guseynov
相关产品推荐
相关产品推荐

