├── result.jpg ├── requirements.txt ├── .gitignore ├── bilibili ├── uids.txt ├── __init__.py └── updates.py ├── bing ├── __init__.py └── wallpaper.py ├── pixiv ├── __init__.py └── ranking.py ├── yamibo ├── __init__.py └── manga.py ├── .github └── workflows │ ├── daily.yml │ ├── weekly.yml │ └── daily_night.yml ├── bot.py ├── cmd.py └── README.md /result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jckling/tg-bot/HEAD/result.jpg -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jckling/tg-bot/HEAD/requirements.txt -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/Lib 2 | **/Scripts 3 | **/.idea 4 | **/__pycache__ 5 | pyvenv.cfg 6 | desktop.ini -------------------------------------------------------------------------------- /bilibili/uids.txt: -------------------------------------------------------------------------------- 1 | 410484677 2 | 666726796 3 | 436596841 4 | 458154143 5 | 458154142 6 | 436596839 7 | 666726798 8 | 666726797 -------------------------------------------------------------------------------- /bing/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : __init__.py 3 | # @Time : 2021/04/16 14:14 4 | # @Author : Jckling 5 | -------------------------------------------------------------------------------- /pixiv/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : __init__.py 3 | # @Time : 2021/04/27 17:37 4 | # @Author : Jckling 5 | -------------------------------------------------------------------------------- /yamibo/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : __init__.py 3 | # @Time : 2021/04/30 10:45 4 | # @Author : Jckling 5 | -------------------------------------------------------------------------------- /bilibili/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : __init__.py 3 | # @Time : 2021/10/24 16:07 4 | # @Author : Jckling 5 | -------------------------------------------------------------------------------- /.github/workflows/daily.yml: -------------------------------------------------------------------------------- 1 | name: Daily push 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '30 22 * * *' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | TOKEN: ${{ secrets.TOKEN }} 15 | CHAT_ID: ${{ secrets.CHAT_ID }} 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Set up Python 3.8 19 | uses: actions/setup-python@v2 20 | with: 21 | python-version: '3.8' 22 | architecture: 'x64' 23 | - name: Install dependencies 24 | run: | 25 | python -m pip install --upgrade pip 26 | pip install -r requirements.txt 27 | - name: Daily push 28 | run: | 29 | python ./cmd.py --daily -------------------------------------------------------------------------------- /.github/workflows/weekly.yml: -------------------------------------------------------------------------------- 1 | name: Weekly push 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '45 23 * * 5' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | TOKEN: ${{ secrets.TOKEN }} 15 | CHAT_ID: ${{ secrets.CHAT_ID }} 16 | PIXIV_REFRESH_TOKEN: ${{ secrets.PIXIV_REFRESH_TOKEN }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up Python 3.8 20 | uses: actions/setup-python@v2 21 | with: 22 | python-version: '3.8' 23 | architecture: 'x64' 24 | - name: Install dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -r requirements.txt 28 | - name: Weekly push 29 | run: | 30 | python ./cmd.py --weekly -------------------------------------------------------------------------------- /.github/workflows/daily_night.yml: -------------------------------------------------------------------------------- 1 | name: Daily Night push 2 | 3 | on: 4 | push: 5 | schedule: 6 | - cron: '00 14 * * *' 7 | 8 | jobs: 9 | build: 10 | 11 | runs-on: ubuntu-latest 12 | 13 | env: 14 | TOKEN: ${{ secrets.TOKEN }} 15 | CHAT_ID: ${{ secrets.CHAT_ID }} 16 | YAMIBO_COOKIES: ${{ secrets.YAMIBO_COOKIES }} 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set timezone 20 | run: sudo timedatectl set-timezone 'Asia/Shanghai' 21 | - name: Echo current time 22 | run: timedatectl 23 | - name: Set up Python 3.8 24 | uses: actions/setup-python@v2 25 | with: 26 | python-version: '3.8' 27 | architecture: 'x64' 28 | - name: Install dependencies 29 | run: | 30 | python -m pip install --upgrade pip 31 | pip install -r requirements.txt 32 | - name: Daily Night push 33 | run: | 34 | python ./cmd.py --daily_night -------------------------------------------------------------------------------- /bing/wallpaper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : wallpaper.py 3 | # @Time : 2021/04/16 13:49 4 | # @Author : Jckling 5 | 6 | from datetime import datetime 7 | 8 | import requests 9 | 10 | # 4k UHD 11 | # BING_API = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=10&nc=1612409408851&pid=hp&FORM=BEHPTB&uhd=1&uhdwidth=3840&uhdheight=2160" 12 | # 1080P HD 13 | BING_API = "https://cn.bing.com/HPImageArchive.aspx?format=js&idx=0&n=10&nc=1612409408851&pid=hp&FORM=BEHPTB" 14 | BING_URL = "https://cn.bing.com" 15 | 16 | 17 | # 获取壁纸 18 | def explore_wallpaper(): 19 | r = requests.get(BING_API) 20 | 21 | # 图片地址 22 | obj = r.json() 23 | image = obj["images"][0] 24 | url = BING_URL + image["url"].split('&')[0] 25 | 26 | # 图片时间 27 | enddate = datetime.strptime(image["enddate"], "%Y%m%d") 28 | 29 | # 版权信息 30 | info = image["copyright"] + "\n" + url.replace("1920x1080", "UHD") 31 | 32 | # 图片内容 33 | r = requests.get(url) 34 | 35 | return r.content, info 36 | 37 | 38 | if __name__ == '__main__': 39 | content, info = explore_wallpaper() 40 | print(info) 41 | -------------------------------------------------------------------------------- /bot.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : bot.py 3 | # @Time : 2021/04/16 13:13 4 | # @Author : Jckling 5 | 6 | import asyncio 7 | import os 8 | 9 | from bing.wallpaper import explore_wallpaper 10 | from pixiv.ranking import weekly_ranking 11 | from telegram import Bot 12 | from yamibo.manga import yuri_manga 13 | 14 | TOKEN = os.environ.get("TOKEN") 15 | CHAT_ID = os.environ.get("CHAT_ID") 16 | 17 | # from telegram.utils import request 18 | # proxy = request.Request(proxy_url='http://127.0.0.1:7890') 19 | # bot = Bot(token=TOKEN, request=proxy) 20 | 21 | bot = Bot(token=TOKEN) 22 | 23 | 24 | async def main(): 25 | # Bing 壁纸 26 | image, info = explore_wallpaper() 27 | await bot.sendPhoto( 28 | chat_id=CHAT_ID, 29 | photo=image, 30 | caption=info 31 | ) 32 | 33 | # Pixiv 插画周榜 34 | lst = weekly_ranking() 35 | await bot.sendMediaGroup( 36 | chat_id=CHAT_ID, 37 | media=lst 38 | ) 39 | 40 | # Yamibo 中文漫画更新 41 | msg = yuri_manga() 42 | await bot.sendMessage( 43 | chat_id=CHAT_ID, 44 | text=msg, 45 | parse_mode="HTML" 46 | ) 47 | 48 | # Bilibili 动态更新 49 | msg = ups_updates() 50 | await bot.sendMessage( 51 | chat_id=CHAT_ID, 52 | text=msg, 53 | parse_mode="HTML", 54 | disable_web_page_preview=True 55 | ) 56 | 57 | 58 | if __name__ == '__main__': 59 | asyncio.run(main()) 60 | -------------------------------------------------------------------------------- /cmd.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : cmd.py 3 | # @Time : 2021/04/29 17:34 4 | # @Author : Jckling 5 | 6 | import argparse 7 | import asyncio 8 | 9 | from bilibili.updates import ups_updates 10 | from bing.wallpaper import explore_wallpaper 11 | from bot import bot, CHAT_ID 12 | from pixiv.ranking import weekly_ranking 13 | from yamibo.manga import yuri_manga 14 | 15 | parser = argparse.ArgumentParser(description='Push message to telegram channel') 16 | parser.add_argument('--daily', action='store_true', help='daily task, including bing_wallpaper') 17 | parser.add_argument('--weekly', action='store_true', help='weekly task, including pixiv_illust_ranking') 18 | parser.add_argument('--daily_night', action='store_true', help='daily night task, including yamibo_manga') 19 | 20 | args = parser.parse_args() 21 | 22 | 23 | async def main(): 24 | try: 25 | if args.daily: 26 | # Bing 壁纸 27 | image, info = explore_wallpaper() 28 | await bot.sendPhoto( 29 | chat_id=CHAT_ID, 30 | photo=image, 31 | caption=info 32 | ) 33 | 34 | if args.weekly: 35 | # Pixiv 插画周榜 36 | lst = weekly_ranking() 37 | await bot.sendMediaGroup( 38 | chat_id=CHAT_ID, 39 | media=lst 40 | ) 41 | 42 | if args.daily_night: 43 | # Yamibo 中文漫画更新 44 | msg = yuri_manga() 45 | await bot.sendMessage( 46 | chat_id=CHAT_ID, 47 | text=msg, 48 | parse_mode="HTML" 49 | ) 50 | 51 | # Bilibili 动态更新 52 | msg = ups_updates() 53 | await bot.sendMessage( 54 | chat_id=CHAT_ID, 55 | text=msg, 56 | parse_mode="HTML", 57 | disable_web_page_preview=True 58 | ) 59 | except Exception as e: 60 | print("Error: %s" % e) 61 | 62 | 63 | asyncio.run(main()) 64 | -------------------------------------------------------------------------------- /pixiv/ranking.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : charts.py 3 | # @Time : 2021/04/27 17:39 4 | # @Author : Jckling 5 | 6 | from pixivpy3 import AppPixivAPI 7 | from telegram import InputMediaPhoto 8 | import os 9 | 10 | USERNAME = os.environ.get("PIXIV_USERNAME") 11 | PASSWORD = os.environ.get("PIXIV_PASSWORD") 12 | REFRESH_TOKEN = os.environ.get("PIXIV_REFRESH_TOKEN") 13 | 14 | tabs = ["综合", "插画", "动图", "漫画", "小说"] 15 | 16 | 17 | def file_too_large(filename): 18 | size = os.path.getsize(filename) 19 | return size >= 10 * 1024 * 1024 20 | 21 | 22 | def download_images(): 23 | # 登录 24 | api = AppPixivAPI() 25 | api.auth(refresh_token=REFRESH_TOKEN) 26 | 27 | # 图像列表 28 | lst = [] 29 | 30 | # [day, week, month, day_male, day_female, week_original, week_rookie, day_manga] 31 | json_result = api.illust_ranking('week') 32 | for illust in json_result.illusts[:10]: 33 | filename = 'illust_{}.jpg'.format(illust.id) 34 | 35 | # 下载图片 36 | if illust.meta_single_page.original_image_url: 37 | api.download(illust.meta_single_page.original_image_url, fname=open(filename, 'wb')) 38 | if file_too_large(filename): 39 | api.download(illust.image_urls.large, fname=open(filename, 'wb')) 40 | else: 41 | api.download(illust.image_urls.large, fname=open(filename, 'wb')) 42 | 43 | if file_too_large(filename): 44 | api.download(illust.image_urls.medium, fname=open(filename, 'wb')) 45 | if file_too_large(filename): 46 | api.download(illust.image_urls.square_medium, fname=open(filename, 'wb')) 47 | 48 | # 打开图片 49 | f = open(filename, 'rb') 50 | info = f"{illust.title} (https://www.pixiv.net/artworks/{illust.id})\n{illust.user.name} (https://www.pixiv.net/users/{illust.user.id})" 51 | photo = InputMediaPhoto(media=f, 52 | caption=info) 53 | lst.append(photo) 54 | 55 | # 打印信息 56 | print("[%s] \t%s\t\n" % (illust.title, illust.image_urls.medium), end="") 57 | 58 | return lst 59 | 60 | 61 | def weekly_ranking(): 62 | return download_images() 63 | 64 | 65 | if __name__ == '__main__': 66 | lst = weekly_ranking() 67 | print(lst) 68 | -------------------------------------------------------------------------------- /bilibili/updates.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : updates.py 3 | # @Time : 2021/10/24 16:09 4 | # @Author : Jckling 5 | 6 | 7 | from datetime import datetime, timedelta 8 | 9 | import requests 10 | 11 | # 22:00 ~ 次日 22:00 12 | today = datetime.now().replace(hour=22, minute=0, second=0) 13 | yesterday = today - timedelta(days=1) 14 | 15 | types = { 16 | 0: "新动态", 17 | 1: "转发动态", 18 | 8: "新投稿", 19 | 16: "短视频", 20 | 64: "新专栏", 21 | 256: "新音频" 22 | } 23 | 24 | 25 | # 从文件读取 26 | def uid_lists(filename="./bilibili/uids.txt"): 27 | with open(filename, 'r') as f: 28 | lines = f.readlines() 29 | return [line.rstrip() for line in lines] 30 | 31 | 32 | # 最新 12 条动态 33 | def updates(uid): 34 | # need_top:1 带置顶, 0 不带置顶 35 | url = f"https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/space_history?host_uid={uid}&offset_dynamic_id=0&need_top=0" 36 | r = requests.get(url).json() 37 | template = "" 38 | for card in r['data']['cards']: 39 | # 发布时间 40 | timestamp = card['desc']['timestamp'] 41 | time = datetime.utcfromtimestamp(timestamp) + timedelta(hours=8) 42 | if time > yesterday and time <= today: 43 | if template == "": 44 | # 个人信息 45 | uid = card['desc']['user_profile']['info']['uid'] 46 | name = card['desc']['user_profile']['info'].get('uname') 47 | # 模板 48 | template = f""" 49 | 🌈 {name} 50 | """ 51 | # 整合成列表 52 | template += format(card, time) 53 | return template 54 | 55 | 56 | # 信息格式 57 | def format(card, time): 58 | # 类型 59 | type = card['desc']['type'] 60 | type = types.get(type, types[0]) 61 | # 地址 62 | url = "https://t.bilibili.com/" + card['desc']['dynamic_id_str'] 63 | # 模板 64 | template = f"""● {time.time().strftime("%H:%M")} {type}:{url} 65 | """ 66 | return template 67 | 68 | 69 | # 获取 up 主动态 70 | def ups_updates(): 71 | message = f"""📢 {yesterday.strftime("%Y.%m.%d")} 22:00 ~ {today.strftime("%Y.%m.%d")} 22:00 Bilibili 动态 72 | """ 73 | for uid in uid_lists(): 74 | message += updates(uid) 75 | return message 76 | 77 | 78 | if __name__ == '__main__': 79 | print(yesterday, today) 80 | msg = ups_updates() 81 | print(msg) 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tg-bot 2 | 3 | 使用 telegram bot 往频道内定时推送消息: 4 | 1. 每天早上 6:30 推送 Bing 壁纸 5 | 2. 每天晚上 22:00 推送百合会漫画更新 6 | 3. 每天晚上 22:00 推送 Bilibili 动态更新 7 | 4. 每周六早上 7:45 推送 Pixiv 周榜 8 | 9 | 10 | 注意事项 11 | - 上面的时间是 Github Action 开始运行的时间,推送到频道内会有一定的延迟; 12 | - Bing 每日壁纸的爬取是根据 UTC 时间,比北京时间慢 8h; 13 | - 百合会论坛内容按积分限制查看,这里并不能绕过,推送是 `昨日 22:00 ~ 今日 22:00` 之间发布的中文漫画(北京时间); 14 | - Bilibili 动态推送是 `昨日 22:00 ~ 今日 22:00` 之间的动态(最多 12 条); 15 | - Pixiv 周榜推送前十的插画。 16 | 17 | 18 | Telegram 推送结果 19 | 20 | ![](result.jpg) 21 | 22 | ## 说明 23 | 24 | **支持本仓库请 star** 25 | 26 | 使用方式同 [jckling/Daily-Bonus](https://github.com/jckling/Daily-Bonus),fork 本仓库然后设置 Actions Secrets 就可以运行了。自定义推送时间修改 .github/workflows/ 目录下对应的配置文件即可,时间格式参考 [events-that-trigger-workflows#schedule](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#schedule) 。 27 | 28 | 29 | | Secret 名称 | 描述 | 30 | | --------------------- | --------------------- | 31 | | TOKEN | telegram bot token | 32 | | CHAT_ID | telegram channel name | 33 | | PIXIV_REFRESH_TOKEN | Pixiv refresh_token | 34 | | YAMIBO_COOKIES | 百合会 Cookie | 35 | 36 | 37 | 1. token 从 [@BotFather](https://telegram.me/botfather) 获取,chat_id 就是 `@bot`(假设频道链接为 https://t.me/bot) 38 | 2. Pixiv `refresh_token` 获取参见👉 [获取 Refresh Token](https://gist.github.com/upbit/6edda27cb1644e94183291109b8a5fde) 39 | - 如果使用 Chrome 可以从 [ChromeDriver - WebDriver for Chrome](https://chromedriver.chromium.org/downloads) 查看和下载对应版本的 chromedriver 40 | 3. 百合会论坛 `Cookie` 获取参见👉 [V2EX Cookie 查看](https://github.com/jckling/Daily-Bonus//#v2ex) 41 | 42 | 43 | Bilibili 动态推送需要在 bilibili/uids.txt 添加 uid: 44 | - 网页端 - 点击头像进入个人空间 - https://space.bilibili.com/uid 45 | - 移动端 - 点击头像进入个人空间 - 个人签名最右侧点击详情查看 46 | 47 | 48 | # 相关链接 49 | 50 | 参阅 51 | - [Bots: An introduction for developers](https://core.telegram.org/bots) 52 | - [python-telegram-bot](https://github.com/python-telegram-bot/python-telegram-bot) 53 | - [upbit/pixivpy](https://github.com/upbit/pixivpy) 54 | - [SK-415/HarukaBot](https://github.com/SK-415/HarukaBot) 55 | 56 | 问题与解决 57 | - [How to Update All Python Packages - ActiveState](https://www.activestate.com/resources/quick-reads/how-to-update-all-python-packages/) 58 | - [python - RuntimeWarning: Enable tracemalloc to get the object allocation traceback - Not using async - Stack Overflow](https://stackoverflow.com/questions/75076069/runtimewarning-enable-tracemalloc-to-get-the-object-allocation-traceback-not) 59 | - [Coroutines and Tasks — Python 3.11.4 documentation](https://docs.python.org/3/library/asyncio-task.html) -------------------------------------------------------------------------------- /yamibo/manga.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # @File : manga.py 3 | # @Time : 2021/04/30 10:48 4 | # @Author : Jckling 5 | 6 | import os 7 | from collections import namedtuple 8 | from datetime import datetime, timedelta 9 | 10 | import requests 11 | from bs4 import BeautifulSoup 12 | from lxml import html 13 | 14 | # cookies 15 | COOKIES = os.environ.get("YAMIBO_COOKIES") 16 | SESSION = requests.Session() 17 | 18 | HEADERS = { 19 | "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9", 20 | "Accept-Encoding": "gzip, deflate, br", 21 | "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,ja;q=0.7,zh-TW;q=0.6,da;q=0.5", 22 | "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36", 23 | "Referer": "https://bbs.yamibo.com/forum-13-1.html", 24 | "sec-ch-ua": '"Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"', 25 | "sec-ch-ua-mobile": "?0", 26 | "Sec-Fetch-Dest": "document", 27 | "Sec-Fetch-Mode": "navigate", 28 | "Sec-Fetch-Site": "same-origin", 29 | "Sec-Fetch-User": "?1", 30 | "Upgrade-Insecure-Requests": "1", 31 | "Connection": "keep-alive", 32 | "Host": "bbs.yamibo.com", 33 | "Cookie": COOKIES 34 | } 35 | 36 | 37 | # 中文百合漫画区 38 | def yuri_manga(): 39 | Manga = namedtuple('Manga', ['title', 'link', 'time']) 40 | Manga_List = [] 41 | for i in range(1, 5): 42 | url = "https://bbs.yamibo.com/forum-30-{}.html" 43 | r = SESSION.get(url.format(i), headers=HEADERS) 44 | 45 | today = datetime.now() 46 | yesterday = today.date() - timedelta(days=1) 47 | 48 | soup = BeautifulSoup(r.text, "lxml") 49 | tree = html.fromstring(str(soup)) 50 | threads = tree.xpath('//tbody[starts-with(@id, "normalthread")]/tr') 51 | for thread in threads: 52 | # 标题 53 | title = thread.find('th[@class="common"]') 54 | if title is None: 55 | title = thread.find('th[@class="new"]') 56 | 57 | try: 58 | title = title.find('a[@class="s xst"]') 59 | except AttributeError: 60 | continue 61 | 62 | # 网址 63 | link = "https://bbs.yamibo.com/" + title.get("href") 64 | 65 | # 发布时间 66 | time = thread.find('td[@class="by"]/em/span') 67 | post_time = datetime.strptime(time.text, "%Y-%m-%d %H:%M") 68 | if post_time.date() == today.date() or \ 69 | (post_time.date() == yesterday and post_time.time() > today.time()): 70 | Manga_List.append(Manga(title.text, link, post_time)) 71 | else: 72 | # 整合成列表 73 | template = """✨ {title} 74 | 75 | """ 76 | message = """📢 {date:%Y-%m-%d} 中文百合漫画 77 | 78 | """.format(date=datetime.now()) 79 | for manga in Manga_List: 80 | message += template.format(title=manga.title, link=manga.link) 81 | 82 | return message 83 | 84 | 85 | if __name__ == '__main__': 86 | msg = yuri_manga() 87 | print(msg) 88 | --------------------------------------------------------------------------------