Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
jromero132 committed Sep 30, 2024
0 parents commit 15db1b5
Show file tree
Hide file tree
Showing 21 changed files with 1,942 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.venv
.vscode
**/__pycache__
manage.py
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# vscode settings
.vscode

# python venv and pycache
.venv
__pycache__

# media (for saving repo space)
src/data/media/pics/*
src/data/media/videos/*

# sensitive data
src/data/bot.session
68 changes: 68 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
hooks:
- id: check-added-large-files
args:
- --maxkb=500
- id: check-case-conflict
- id: check-docstring-first
- id: check-json
- id: check-toml
- id: check-yaml
- id: end-of-file-fixer
- id: mixed-line-ending
args:
- --fix=lf
- id: trailing-whitespace

- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
files: ".*"
args:
- --profile=black
- --project=telegram-auto-texter

- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.11.2
hooks:
- id: mypy
files: ".*"
additional_dependencies:
- types-pyyaml

- repo: https://github.com/pycqa/pydocstyle
rev: 6.3.0
hooks:
- id: pydocstyle
args:
- --convention=google
exclude: "docs|tests"

- repo: https://github.com/asottile/pyupgrade
rev: v3.17.0
hooks:
- id: pyupgrade
args:
- --py310-plus

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.8
hooks:
- id: ruff
args:
- --fix
- --exit-non-zero-on-fix
- --show-fixes
- --line-length=100
- id: ruff-format
args:
- --line-length=100

- repo: https://github.com/adrienverge/yamllint.git
rev: v1.35.1
hooks:
- id: yamllint
exclude: "tests"
3 changes: 3 additions & 0 deletions config.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[Telegram]
api_id = API_ID
api_hash = API_HASH
17 changes: 17 additions & 0 deletions dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM ubuntu:22.04

ENV DEBIAN_FRONTEND=noninteractive \
TZ="Europe/Stockholm"

RUN apt-get update && \
apt-get install -y --no-install-recommends \
python3 \
python3-pip \
tzdata && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

WORKDIR /telegram-auto-texter
ADD . .
RUN pip install -r requirements.txt
CMD ["python3", "-u", "main.py"]
21 changes: 21 additions & 0 deletions license
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2024 Jose A. Romero

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
183 changes: 183 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
"""This module serves as the main entry point for the Telegram bot application.
It handles the initialization of the bot, sets up scheduled tasks for sending messages and media,
and manages user interactions. The module integrates with the worker functions to perform various
tasks such as sending greetings, media items, and reminders, while utilizing the APScheduler for
task scheduling.
"""

import asyncio
from configparser import ConfigParser, ExtendedInterpolation

from apscheduler.schedulers.asyncio import AsyncIOScheduler
from telethon import TelegramClient, events
from telethon.tl.custom.message import Message

from src import worker

config = ConfigParser(interpolation=ExtendedInterpolation())
config.read("config.ini")

client = TelegramClient(
"src/data/bot", config.get("Telegram", "api_id"), config.get("Telegram", "api_hash")
).start()
print("Telegram user bot is now running...")


@client.on(events.NewMessage("me", pattern="/health"))
async def handle_health(event: Message | events.NewMessage):
"""Handles the /health command and responds with the application's health status.
This asynchronous function listens for messages containing the /health command and replies with
the current health status of the application. It provides a simple way for users to check if the
bot is operational.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the reply.
"""
await event.reply(worker.health())


@client.on(events.NewMessage("me", pattern="/greeting_info"))
async def handle_greeting_info(event: Message | events.NewMessage):
"""Handles the /greeting_info command and responds with the next greeting time.
This asynchronous function listens for messages containing the /greeting_info command and
replies with the scheduled time for the next greeting. It provides users with information about
when they can expect the next greeting message.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the reply.
"""
await event.reply(f"Next greeting at {worker.next_greeting_time}")


@client.on(events.NewMessage("me", pattern="/send_greeting"))
async def handle_send_greeting(event: Message | events.NewMessage):
"""Handles the /send_greeting command to send a morning greeting.
This asynchronous function listens for messages containing the /send_greeting command and
triggers the sending of a morning greeting via the Telegram client. It then replies to the user
to confirm that the action has been completed.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the greeting or the reply.
"""
await worker.send_morning_greeting(client)
await event.reply("Done!")


@client.on(events.NewMessage("me", pattern="/test_greeting"))
async def handle_test_greeting(event: Message | events.NewMessage):
"""Handles the /test_greeting command to send a test morning greeting.
This asynchronous function listens for messages containing the /test_greeting command and
triggers the sending of a morning greeting via the Telegram client without marking it as used.
It then replies to the user to confirm that the action has been completed.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the greeting or the reply.
"""
await worker.send_morning_greeting(client, user_id="me", set_as_used=False)
await event.reply("Done!")


@client.on(events.NewMessage("me", pattern="/afternoon_media_info"))
async def handle_afternoon_media(event: Message | events.NewMessage):
"""Handles the /afternoon_media_info command and responds with the next media time.
This asynchronous function listens for messages containing the /afternoon_media_info command
and replies with the scheduled time for the next afternoon media. It provides users with
information about when they can expect the next media item.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the reply.
"""
await event.reply(f"Next greeting at {worker.next_afternoon_media_time}")


@client.on(events.NewMessage("me", pattern="/send_afternoon_media"))
async def handle_send_afternoon_media(event: Message | events.NewMessage):
"""Handles the /send_afternoon_media command to send an afternoon media item.
This asynchronous function listens for messages containing the /send_afternoon_media command
and triggers the sending of an afternoon media item via the Telegram client. It then replies to
the user to confirm that the action has been completed.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the media or the reply.
"""
await worker.send_afternoon_media(client)
await event.reply("Done!")


@client.on(events.NewMessage("me", pattern="/test_afternoon_media"))
async def handle_test_afternoon_media(event: Message | events.NewMessage):
"""Handles the /test_afternoon_media command to send a test afternoon media item.
This asynchronous function listens for messages containing the /test_afternoon_media command
and triggers the sending of an afternoon media item via the Telegram client without marking it
as used. It then replies to the user to confirm that the action has been completed.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the media or the reply.
"""
await worker.send_afternoon_media(client, user_id="me", set_as_used=False)
await event.reply("Done!")


@client.on(events.NewMessage("me", pattern="/stats"))
async def handle_stats(event: Message | events.NewMessage):
"""Handles the /stats command to send statistics to the user.
This asynchronous function listens for messages containing the /stats command and triggers the
sending of statistics related to the application via the Telegram client. It provides users with
insights into the current state of the application.
Args:
event (Message | events.NewMessage): The event object representing the incoming message.
Raises:
Exception: If there is an error while sending the statistics.
"""
await worker.send_stats(client, user_id="me")


async def main():
"""Main entry point for starting the Telegram bot and scheduling tasks.
This asynchronous function initializes the scheduler and starts the various tasks for sending
morning greetings, afternoon media, and pill reminders. It then enters an infinite loop to keep
the application running and responsive.
"""
scheduler = AsyncIOScheduler()
worker.start_sending_morning_greeting(scheduler, client, try_today=True)
worker.start_sending_afternoon_media(scheduler, client, try_today=True)
worker.start_sending_pills_reminder(scheduler, client)
scheduler.start()
while True:
await asyncio.sleep(1)


client.loop.run_until_complete(main())
Loading

0 comments on commit 15db1b5

Please sign in to comment.