├── .gitattributes ├── README.md ├── baidu_trans.py ├── config.py ├── main.py ├── pictrans copy.py ├── pictrans.py ├── requirements.txt └── rss.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Twitter to QQ (Python) 2 | 3 | 这个Python脚本可以将一个或多个推特账号的更新推送至QQ,并且可以机翻,使用python方便扩展更多功能。 4 | 5 | ## 依赖 6 | 7 | 首先需要 酷Q Pro (使用酷Q Air将无法发送图片)并在 [这里](https://cqhttp.cc/docs/4.12/#/Configuration) 按照教程配置好 CQhttp 插件 8 | 爬虫部分采用 [RSSHub](https://rsshub.app/)。 9 | Python依赖在request.txt中 10 | 11 | ## 使用方法 12 | 13 | ### 安装 14 | 15 | ```bash 16 | git clone git@github.com:Audirntttttttt/TwitterToQQPy.git 17 | cd TwitterToQQPy 18 | pip install -r requirements.txt 19 | ``` 20 | 21 | ### 配置 22 | 23 | #### CoolQ的配置 24 | 25 | 在酷Q目录下`\data\app\io.github.richardchien.coolqhttpapi\config`的json文件中,修改以下选项: 26 | 27 | ```json 28 | "port": 你使用的接口, 29 | "use_http": true, 30 | "use_ws": false, 31 | "access_token": "your token", 32 | "secret": "your secret", 33 | ``` 34 | 在本项目的目录下,修改config.py: 35 | ```python 36 | access_token = 'your access_token' 37 | secret = 'your secret' 38 | api_root = '酷Qhttp运行的地址' 39 | ``` 40 | 与上方的配置相对应 41 | 42 | #### 百度翻译API的配置 43 | 44 | 在[这里](http://api.fanyi.baidu.com/api/trans/product/index)申请百度翻译的API的接口,并修改config.py: 45 | 46 | ```python 47 | #百度翻译的appid与secretKey 48 | 49 | appid = 'your appid' 50 | secretKey = 'your secretkey' 51 | ``` 52 | 53 | #### 发送与接收的配置 54 | 55 | 在config.py中设置,config.py有详尽的说明。 56 | 57 | ### 启动文件 58 | 59 | 配置完成后 60 | 61 | ```bash 62 | python main.py 63 | ``` 64 | 65 | 或者 66 | 67 | ```bash 68 | pm2 start main.py --interpreter=python3 69 | ``` 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /baidu_trans.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import hashlib 3 | import random 4 | import config 5 | 6 | def genearteMD5(str): 7 | hl = hashlib.md5() 8 | 9 | hl.update(str.encode(encoding='utf-8')) 10 | 11 | return hl.hexdigest() 12 | 13 | def trans(q): 14 | if q=="": 15 | return "" 16 | fromLang = 'auto' 17 | toLang = 'zh' 18 | salt = random.randint(32768, 65536) 19 | appid = config.appid 20 | secretKey = config.secretKey 21 | 22 | sign = appid+q+str(salt)+secretKey 23 | sign = genearteMD5(sign) 24 | 25 | payload = {"q" : q ,"from" : fromLang ,"to" : toLang , 26 | "appid" : appid, "salt" : salt, "sign" : sign} 27 | 28 | url = "http://api.fanyi.baidu.com/api/trans/vip/translate" 29 | 30 | r = requests.get(url, params=payload) 31 | 32 | text = eval(r.text) 33 | #print(text) 34 | #text0 = text['trans_result'][0]['dst'] 35 | texts = '' 36 | try: 37 | for i in text['trans_result']: 38 | texts = texts+i['dst']+'\n' 39 | except KeyError as identifier: 40 | pass 41 | 42 | return texts 43 | 44 | 45 | if __name__ == "__main__": 46 | print(trans('''【お知らせ】\n『Shiny Smily Story』のコール練習動画(試聴動画尺ver.)を投稿しました。\n近々...必要かもしれません?''')) -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | #百度翻译的appid与secretKey 2 | 3 | appid = 'your appid' 4 | secretKey = 'your secretkey' 5 | 6 | #发送与接收设置 7 | #其中rss源为rsshub的router 8 | #格式list[ [rsshub源1,[接收者1,接收者2,接收者3],True(翻译与否)] , [rss源2,[接收者2,接收者4,接收者5]] ] 9 | #例子[ ['twitter/user/ichika_mo',[123456,456789],True], 10 | # ['twitter/user/shirakamifubuki',[123456,456789],False] ] 11 | 12 | QQ_Group = [] 13 | QQ_Private = [] 14 | 15 | #CQHttp的控制 16 | 17 | access_token = 'your access_token' 18 | secret = 'your secret' 19 | api_root = '酷Qhttp运行的地址' #http 20 | 21 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import config 2 | from apscheduler.schedulers.blocking import BlockingScheduler 3 | from datetime import datetime 4 | from cqhttp import CQHttp 5 | import rss 6 | import time 7 | 8 | time_in_minutes = 20 9 | 10 | def job(): 11 | print(datetime.now().strftime("%Y-%m-%d %H:%M:%S")) 12 | bot = CQHttp(api_root=config.api_root, 13 | access_token = config.access_token, 14 | secret = config.secret) 15 | 16 | for i in config.QQ_Group: 17 | twis = rss.twigen(i[0],time_in_minutes,i[2]) 18 | for k in twis: 19 | print(k) 20 | for j in i[1]: 21 | bot.send_group_msg(group_id=j, message=k) 22 | time.sleep(5) 23 | 24 | for i in config.QQ_Private: 25 | twis = rss.twigen(i[0],time_in_minutes,i[2]) 26 | for k in twis: 27 | print(k) 28 | for j in i[1]: 29 | bot.send_private_msg(user_id=j, message=k) 30 | time.sleep(5) 31 | 32 | 33 | 34 | if __name__ == "__main__": 35 | job() 36 | sched = BlockingScheduler() 37 | sched.add_job(job, 'interval',minutes = 20) 38 | sched.start() 39 | 40 | 41 | -------------------------------------------------------------------------------- /pictrans copy.py: -------------------------------------------------------------------------------- 1 | import os, sqlite3 2 | import requests 3 | import time 4 | from urllib import parse 5 | 6 | class picbase: 7 | def __init__(self): 8 | super().__init__() 9 | self.db_file = os.path.join(os.path.dirname(__file__), "pic.db") 10 | if os.path.isfile(self.db_file): 11 | os.remove(self.db_file) 12 | if not self.extable(): 13 | self.oritable() 14 | 15 | def extable(self): 16 | SQL="SELECT count(*) FROM sqlite_master WHERE type='table' AND name='pics'" 17 | conn = sqlite3.connect(self.db_file) 18 | cursor = conn.cursor() 19 | cursor.execute(SQL) 20 | values = cursor.fetchall() 21 | values=bool(values[0][0]) 22 | cursor.close() 23 | conn.commit() 24 | conn.close() 25 | return values 26 | 27 | 28 | 29 | def oritable(self): 30 | try: 31 | conn = sqlite3.connect(self.db_file) 32 | cursor = conn.cursor() 33 | cursor.execute(''' 34 | create table pics(hash VARCHAR(20) primary key, 35 | time INT, 36 | url VARCHAR(255), 37 | delurl VARCHAR(255)) 38 | ''') 39 | #cursor.execute() 40 | 41 | except Exception as e: 42 | print('建立错误:', e) 43 | finally: 44 | cursor.close() 45 | conn.commit() 46 | conn.close() 47 | pass 48 | 49 | def pictobase(self,url,type): 50 | if type == 2: 51 | imgname = url 52 | else: 53 | if type==1: 54 | url = url.split("?") 55 | img_src = url[0] 56 | res = dict(parse.parse_qsl(url[1])) 57 | img = requests.get(img_src,res) 58 | imgname = str(time.time())+"."+res["format"] 59 | elif type==0: 60 | img = requests.get(url) 61 | imgname = str(time.time())+url[len(url)-4:len(url)] 62 | with open(imgname, 'ab') as f: 63 | f.write(img.content) 64 | f.close() 65 | 66 | url='https://sm.ms/api/upload' 67 | 68 | file_obj=open(os.path.join(os.path.dirname(__file__),imgname),'rb') 69 | file={'smfile':file_obj} 70 | data_result=requests.post(url,data=None,files=file) 71 | a = data_result.json() 72 | print(a) 73 | hash = a["data"]["hash"] 74 | timeinserver = a["data"]["filename"].split(".")[0] 75 | url = a["data"]["url"] 76 | delete = a["data"]["delete"] 77 | sql = "INSERT INTO pics VALUES("+" '"+hash+"',"+" '"+timeinserver+"',"+" '"+url+"',"+" '"+delete+"')" 78 | try: 79 | conn = sqlite3.connect(self.db_file) 80 | cursor = conn.cursor() 81 | cursor.execute(sql) 82 | #cursor.execute() 83 | 84 | except Exception as e: 85 | print('插入错误:', e) 86 | return "faile" 87 | finally: 88 | cursor.close() 89 | conn.commit() 90 | conn.close() 91 | return url 92 | 93 | 94 | def listpic(self): 95 | try: 96 | conn = sqlite3.connect(self.db_file) 97 | cursor = conn.cursor() 98 | cursor.execute('select * from pics') 99 | values = cursor.fetchall() 100 | print(values) 101 | return 1 102 | except Exception as e: 103 | print('查询错误:', e) 104 | return -1 105 | finally: 106 | cursor.close() 107 | conn.commit() 108 | conn.close() 109 | 110 | -------------------------------------------------------------------------------- /pictrans.py: -------------------------------------------------------------------------------- 1 | import os,re 2 | import requests 3 | import time 4 | from urllib import parse 5 | 6 | class picbase: 7 | def __init__(self): 8 | super().__init__() 9 | 10 | def pictobase(self,url,type): 11 | if type == 2: 12 | imgname = url 13 | else: 14 | if type==1: 15 | url = url.split("?") 16 | img_src = url[0] 17 | res = dict(parse.parse_qsl(url[1])) 18 | img = requests.get(img_src,res) 19 | imgname = str(time.time())+"."+res["format"] 20 | elif type==0: 21 | img = requests.get(url) 22 | imgname = str(time.time())+url[len(url)-4:len(url)] 23 | with open(imgname, 'ab') as f: 24 | f.write(img.content) 25 | f.close() 26 | print(os.path.join(os.path.dirname(__file__),imgname)) 27 | url='https://sm.ms/api/upload' 28 | file_obj=open(os.path.join(os.path.dirname(__file__),imgname),'rb') 29 | file={'smfile':file_obj} 30 | data_result=requests.post(url,data=None,files=file) 31 | a = data_result.json() 32 | print(a) 33 | if a["success"] == "True": 34 | url = a["data"]["url"] 35 | else: 36 | string = a["message"] 37 | pattern = re.compile(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+') 38 | url = re.findall(pattern,string)[0] 39 | return url 40 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | APScheduler==3.6.0 2 | cqhttp==1.2.3 3 | requests==2.21.0 4 | feedparser==5.2.1 5 | -------------------------------------------------------------------------------- /rss.py: -------------------------------------------------------------------------------- 1 | import baidu_trans 2 | import feedparser 3 | import time 4 | import re 5 | import pictrans 6 | from datetime import datetime 7 | 8 | 9 | def search_all(patter,strs): 10 | str = [] 11 | num = patter.find('"') 12 | while re.search(patter,strs)!=None: 13 | result = re.search(patter,strs).span() 14 | str_one = strs[result[0]+num:result[1]-1] 15 | str.append(str_one) 16 | strs = strs[result[1]+1:len(strs)] 17 | 18 | return(str) 19 | 20 | def content(text): 21 | p = r'<.*?>' 22 | return re.sub(p," ",text) 23 | 24 | def twigen(url,minute,trans): 25 | messegess = [] 26 | url = "https://rsshub.app/"+url 27 | d = feedparser.parse(url) 28 | print("success") 29 | 30 | 31 | timenow = datetime.utcnow().timestamp() 32 | print(timenow) 33 | xxx = d.entries 34 | print(len(xxx)) 35 | for i in xxx: 36 | print(time.mktime(i.published_parsed)) 37 | if timenow-time.mktime(i.published_parsed)<60*minute: 38 | timesss = i.published_parsed 39 | user = d.feed.title 40 | twitter = i.summary_detail.value 41 | print(1) 42 | 43 | vedio_p = r'video src=\".*?\"' 44 | imgs_p = r'img src=\".*?\"' 45 | covers_p = r'poster=\".*?\"' 46 | url_p = r'href=\".*?\"' 47 | 48 | twitte_content = content(twitter) 49 | 50 | #print(twitte_transed) 51 | print(2) 52 | #替换
53 | #翻译 54 | 55 | twitte_media = twitter 56 | 57 | vedio = search_all(vedio_p,twitte_media) 58 | #print(vedio) 59 | imgs = search_all(imgs_p,twitte_media) 60 | #print(imgs) 61 | covers = search_all(covers_p,twitte_media) 62 | #print(covers) 63 | url = search_all(url_p,twitte_media) 64 | #print(url) 65 | 66 | time.sleep(2) 67 | 68 | medias = "" 69 | pictrsnsformer = pictrans.picbase() 70 | if len(imgs) != 0: 71 | medias = medias + "图片:\n" 72 | for i in imgs: 73 | i = pictrsnsformer.pictobase(i,1) 74 | medias = medias + '[CQ:image,cache=0,file=' + i + ']\n' 75 | if len(vedio)!=0 : 76 | medias = medias + '视频:\n' 77 | medias = medias + "视频链接:\n" 78 | medias = medias + vedio[0] 79 | medias = medias + "视频封面:\n" 80 | covers = pictrsnsformer.pictobase(covers[0],0) 81 | medias = medias + '[CQ:image,cache=0,file=' + covers + ']\n' 82 | if len(url)!=0 : 83 | medias = medias + "链接:\n" 84 | for i in url: 85 | medias = medias + i + "\n" 86 | 87 | timesss = time.asctime(timesss) 88 | 89 | if(trans): 90 | twitte_transed = baidu_trans.trans(twitte_content) 91 | messeges = user + "更新:\n\n原文:\n" + twitte_content + "\n\n译文(机翻):\n" + twitte_transed + "\n" + medias +str(timesss) 92 | else: 93 | 94 | messeges = user + "更新:\n\n原文:\n" + twitte_content + "\n" + medias +str(timesss) 95 | #print(messeges) 96 | 97 | messegess.append(messeges) 98 | print(len(messegess)) 99 | time.sleep(10) 100 | print("-----------------------") 101 | 102 | return messegess 103 | 104 | --------------------------------------------------------------------------------