Dockerize.

Signed-off-by: Abdulkadir Furkan Şanlı <me@abdulocra.cy>
This commit is contained in:
Abdulkadir Furkan Şanlı 2024-01-21 10:38:05 +01:00
parent 3d4483539b
commit 2c82286bfe
Signed by: afk
SSH Key Fingerprint: SHA256:s1hULLl4YWdqU501MUfGe1CAG/m1pf9Cs6vFsqeTNHk
5 changed files with 54 additions and 18 deletions

11
Dockerfile Normal file
View File

@ -0,0 +1,11 @@
FROM python:3
ENV PYTHONUNBUFFERED=1
WORKDIR /usr/src/app
COPY main.py requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
CMD ["python", "./main.py"]

6
compose.yml Normal file
View File

@ -0,0 +1,6 @@
services:
parkerbot:
build: .
env_file: .env
volumes:
- ./data:/data

View File

@ -1,5 +1,5 @@
# Path of sqlite3 file to use. # Path for persistent app data.
DB_PATH = "" DATA_DIR = "/data"
# Matrix homeserver URL. # Matrix homeserver URL.
MATRIX_SERVER = "" MATRIX_SERVER = ""
# Matrix room to monitor. # Matrix room to monitor.
@ -9,6 +9,6 @@ MATRIX_USER = ""
# Password for bot's Matrix user. # Password for bot's Matrix user.
MATRIX_PASSWORD = "" MATRIX_PASSWORD = ""
# Title of the playlists created, date of the week's Monday will be appended. # Title of the playlists created, date of the week's Monday will be appended.
PLAYLIST_TITLE = "" YOUTUBE_PLAYLIST_TITLE = ""
# YouTube API client secret json path. # YouTube API client secret json path.
YOUTUBE_CLIENT_SECRETS_FILE = os.getenv("YOUTUBE_CLIENT_SECRETS_FILE") YOUTUBE_CLIENT_SECRETS_FILE = ""

44
main.py
View File

@ -5,22 +5,27 @@ import asyncio
import os import os
import pickle import pickle
import re import re
import signal
import sqlite3 import sqlite3
import sys
from datetime import datetime, timedelta from datetime import datetime, timedelta
from dotenv import load_dotenv
from google.auth.transport.requests import Request from google.auth.transport.requests import Request
from google_auth_oauthlib.flow import InstalledAppFlow from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build from googleapiclient.discovery import build
from nio import AsyncClient, ClientConfig, RoomMessageText, SyncResponse from nio import AsyncClient, RoomMessageText, SyncResponse
DATA_DIR = os.getenv("DATA_DIR", "./")
DB_PATH = os.path.join(DATA_DIR, "parkerbot.sqlite3")
TOKEN_PATH = os.path.join(DATA_DIR, "sync_token")
PICKLE_PATH = os.path.join(DATA_DIR, "token.pickle")
load_dotenv()
DB_PATH = os.getenv("DB_PATH")
MATRIX_SERVER = os.getenv("MATRIX_SERVER") MATRIX_SERVER = os.getenv("MATRIX_SERVER")
MATRIX_ROOM = os.getenv("MATRIX_ROOM") MATRIX_ROOM = os.getenv("MATRIX_ROOM")
MATRIX_USER = os.getenv("MATRIX_USER") MATRIX_USER = os.getenv("MATRIX_USER")
MATRIX_PASSWORD = os.getenv("MATRIX_PASSWORD") MATRIX_PASSWORD = os.getenv("MATRIX_PASSWORD")
PLAYLIST_TITLE = os.getenv("PLAYLIST_TITLE")
YOUTUBE_PLAYLIST_TITLE = os.getenv("YOUTUBE_PLAYLIST_TITLE")
YOUTUBE_CLIENT_SECRETS_FILE = os.getenv("YOUTUBE_CLIENT_SECRETS_FILE") YOUTUBE_CLIENT_SECRETS_FILE = os.getenv("YOUTUBE_CLIENT_SECRETS_FILE")
YOUTUBE_API_SERVICE_NAME = "youtube" YOUTUBE_API_SERVICE_NAME = "youtube"
YOUTUBE_API_VERSION = "v3" YOUTUBE_API_VERSION = "v3"
@ -61,9 +66,9 @@ def define_tables():
def get_authenticated_service(): def get_authenticated_service():
"""Get an authentivated YouTube service.""" """Get an authentivated YouTube service."""
credentials = None credentials = None
# The file token.pickle stores the user's access and refresh tokens. # Stores the user's access and refresh tokens.
if os.path.exists("token.pickle"): if os.path.exists(PICKLE_PATH):
with open("token.pickle", "rb") as token: with open(PICKLE_PATH, "rb") as token:
credentials = pickle.load(token) credentials = pickle.load(token)
# If there are no valid credentials available, let the user log in. # If there are no valid credentials available, let the user log in.
@ -77,7 +82,7 @@ def get_authenticated_service():
) )
credentials = flow.run_local_server(port=8080) credentials = flow.run_local_server(port=8080)
# Save the credentials for the next run # Save the credentials for the next run
with open("token.pickle", "wb") as token: with open(PICKLE_PATH, "wb") as token:
pickle.dump(credentials, token) pickle.dump(credentials, token)
return build("youtube", "v3", credentials=credentials) return build("youtube", "v3", credentials=credentials)
@ -111,7 +116,7 @@ def make_playlist(youtube, title):
def get_or_make_playlist(youtube, monday_date): def get_or_make_playlist(youtube, monday_date):
"""Get ID of playlist for given Monday's week, make if doesn't exist.""" """Get ID of playlist for given Monday's week, make if doesn't exist."""
playlist_title = f"{PLAYLIST_TITLE} {monday_date.strftime('%Y-%m-%d')}" playlist_title = f"{YOUTUBE_PLAYLIST_TITLE} {monday_date.strftime('%Y-%m-%d')}"
# Check if playlist exists in the database # Check if playlist exists in the database
cursor.execute( cursor.execute(
@ -216,20 +221,23 @@ async def message_callback(client, room, event):
async def sync_callback(response): async def sync_callback(response):
"""Save Matrix sync token."""
# Save the sync token to a file or handle it as needed # Save the sync token to a file or handle it as needed
with open("sync_token", "w") as f: with open(TOKEN_PATH, "w") as f:
f.write(response.next_batch) f.write(response.next_batch)
def load_sync_token(): def load_sync_token():
"""Get an existing Matrix sync token if it exists."""
try: try:
with open("sync_token", "r") as file: with open(TOKEN_PATH, "r") as file:
return file.read().strip() return file.read().strip()
except FileNotFoundError: except FileNotFoundError:
return None return None
async def get_client(): async def get_client():
"""Returns configured and logged in Matrix client."""
client = AsyncClient(MATRIX_SERVER, MATRIX_USER) client = AsyncClient(MATRIX_SERVER, MATRIX_USER)
client.add_event_callback( client.add_event_callback(
lambda room, event: message_callback(client, room, event), RoomMessageText lambda room, event: message_callback(client, room, event), RoomMessageText
@ -240,12 +248,24 @@ async def get_client():
return client return client
def sigterm_handler(signum, frame):
"""Gracefully stop syncing on SIGTERM."""
asyncio.get_event_loop().stop()
async def main(): async def main():
"""Get DB and Matrix client ready, and start syncing.""" """Get DB and Matrix client ready, and start syncing."""
define_tables() define_tables()
client = await get_client() client = await get_client()
signal.signal(signal.SIGTERM, sigterm_handler)
sync_token = load_sync_token() sync_token = load_sync_token()
try:
await client.sync_forever(30000, full_state=True, since=sync_token) await client.sync_forever(30000, full_state=True, since=sync_token)
finally:
conn.close()
await client.logout()
sys.exit()
if __name__ == "__main__": if __name__ == "__main__":
asyncio.run(main()) asyncio.run(main())

View File

@ -1,4 +1,3 @@
matrix-nio matrix-nio
google-auth-oauthlib google-auth-oauthlib
google-api-python-client google-api-python-client
python-dotenv