├── 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 | 
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 |
--------------------------------------------------------------------------------