Skip to content

Commit 8863aa7

Browse files
committed
add sdk schedule command
1 parent 716d9cb commit 8863aa7

File tree

6 files changed

+100
-5
lines changed

6 files changed

+100
-5
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,5 @@ __pycache__/
66
/.venvs/
77
/.task/
88
/tmp.py
9+
/.env
10+
/*.session

Taskfile.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ vars:
88

99
env:
1010
FLIT_ROOT_INSTALL: "1"
11+
dotenv:
12+
- ".env"
1113

1214
tasks:
1315
install:flitenv:

pyproject.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,16 @@ requires-python = ">=3.8"
1414
dynamic = ["version", "description"]
1515
classifiers = ["Private :: Do Not Upload"]
1616
dependencies = [
17+
"aiotask-context",
18+
"IPython",
1719
"jinja2",
1820
"jsonschema",
1921
"markdown-it-py",
22+
"pytz",
2023
"pyyaml",
2124
"requests",
22-
"IPython",
2325
"simplejson",
24-
"pytz",
25-
"aiotask-context",
26+
"telethon",
2627
]
2728

2829
[project.optional-dependencies]

sdk/commands/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
from ._run_code import RunCodeCommand
55
from ._table import TableCommand
66
from ._telegram import TelegramCommand
7+
from ._schedule import ScheduleCommand
78

89

910
__all__ = ['Command', 'COMMANDS']
1011

1112
COMMANDS = (
1213
HTMLCommand,
1314
RunCodeCommand,
15+
ScheduleCommand,
1416
TableCommand,
1517
TelegramCommand,
1618
CheckAllCommand,

sdk/commands/_schedule.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
2+
from __future__ import annotations
3+
4+
import argparse
5+
import asyncio
6+
from datetime import date, datetime, timedelta, time
7+
from functools import cached_property
8+
import os
9+
from pathlib import Path
10+
from telethon import TelegramClient
11+
from telethon.types import Message
12+
from zoneinfo import ZoneInfo
13+
14+
from ..post import Post
15+
from ._command import Command
16+
17+
CHANNEL = 'pythonetc'
18+
TZ = ZoneInfo('Europe/Amsterdam')
19+
20+
21+
class ScheduleCommand(Command):
22+
"""Add post in scheduled messages in the Telegram channel.
23+
"""
24+
name = 'schedule'
25+
26+
@classmethod
27+
def init_parser(cls, parser: argparse.ArgumentParser) -> None:
28+
parser.add_argument('post', type=Path)
29+
30+
def run(self) -> int:
31+
post = Post.from_path(self.args.post)
32+
assert not post.sequence
33+
assert not post.markdown.has_images()
34+
assert not post.buttons
35+
36+
if 'API_ID' not in os.environ:
37+
self.warn(
38+
'API_ID and API_HASH env vars required, '
39+
'you can get them at https://my.telegram.org/apps',
40+
)
41+
return 1
42+
43+
error = asyncio.run(self._schedule(post))
44+
if error:
45+
self.warn(error)
46+
return 1
47+
return 0
48+
49+
async def _schedule(self, post: Post) -> str | None:
50+
# check if the publish date is known and in the future
51+
if not post.published:
52+
return 'post must have `published` set'
53+
if post.published < date.today():
54+
return 'post already published'
55+
56+
async with self._client:
57+
scheduled = await self._get_scheduled()
58+
if post.published in scheduled:
59+
return 'post already scheduled'
60+
61+
# schedule the post
62+
publish_at = datetime.combine(post.published, time(16, 0, tzinfo=TZ))
63+
in10m = datetime.now(TZ) + timedelta(minutes=10)
64+
if publish_at < in10m:
65+
publish_at = in10m
66+
await self._client.send_message(
67+
CHANNEL,
68+
post.telegram_markdown,
69+
schedule=publish_at,
70+
link_preview=False,
71+
)
72+
self.print(f'post is scheduled to be published at {post.published}')
73+
return None
74+
75+
async def _get_scheduled(self) -> list[date]:
76+
result = []
77+
message: Message
78+
async for message in self._client.iter_messages(CHANNEL, scheduled=True):
79+
assert message.date
80+
result.append(message.date.date())
81+
return result
82+
83+
@cached_property
84+
def _client(self) -> TelegramClient:
85+
return TelegramClient(
86+
'bot',
87+
api_id=os.environ['API_ID'],
88+
api_hash=os.environ['API_HASH'],
89+
)

sdk/post.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
from datetime import date
77
from functools import cached_property
88
from pathlib import Path
9-
from typing import Optional
109

1110
import jsonschema
1211
import yaml
@@ -163,7 +162,7 @@ def telegram_markdown(self) -> str:
163162

164163
return copy.text
165164

166-
def self_in_sequence(self) -> Optional[PostOfSequence]:
165+
def self_in_sequence(self) -> PostOfSequence | None:
167166
if self.sequence is None:
168167
return None
169168
found = [

0 commit comments

Comments
 (0)