Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,16 @@ platforms such as GitHub discussions/issues might be added in the future.
* Set up an application at [reddit apps](https://www.reddit.com/prefs/apps/).
* The redirect uri should be https://localhost:8080
* Take note of the `client_id` and `client_secret`

### Environment Variables

* Add the following as environment variables or in a `.env` file (use `sample.env` as an example).
:exclamation: if using Docker these can be arguments.
:warning: Never publicly expose your tokens, secrets, or ids.
* Add the following as environment variables or in a `.env` file (use `sample.env` as an example).

> [!TIP]
> If using Docker, these can be arguments.

> [!WARNING]
> Never publicly expose your tokens, secrets, or ids.

| variable | required | default | description |
|----------------------------------|----------|------------------------------------------------------|--------------------------------------------------------------------------------------------|
Expand All @@ -45,6 +49,7 @@ platforms such as GitHub discussions/issues might be added in the future.
| DISCORD_CLIENT_ID | True | `None` | Discord OAuth2 client id. |
| DISCORD_CLIENT_SECRET | True | `None` | Discord OAuth2 client secret. |
| DISCORD_GITHUB_STATUS_CHANNEL_ID | True | `None` | Channel ID to send GitHub status updates to. |
| DISCORD_LEVEL_UP_CHANNEL_ID | False | `None` | Channel ID to send user level up messages to. |
| DISCORD_REDDIT_CHANNEL_ID | True | `None` | Channel ID to send Reddit post updates to. |
| DISCORD_REDIRECT_URI | False | `https://localhost:8080/discord/callback` | The redirect uri for OAuth2. Must be publicly accessible. |
| DISCORD_SPONSORS_CHANNEL_ID | True | `None` | Channel ID to send sponsorship updates to. |
Expand Down
1 change: 1 addition & 0 deletions sample.env
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ SUPPORT_COMMANDS_BRANCH=master

# Secret settings
DISCORD_BOT_TOKEN=
DISCORD_LEVEL_UP_CHANNEL_ID=
GRAVATAR_EMAIL=

# discord/github oauth2
Expand Down
124 changes: 78 additions & 46 deletions src/common/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

# lib imports
import git
from tinydb import TinyDB
from tinydb import TinyDB, Query
from tinydb.storages import JSONStorage
from tinydb.middlewares import CachingMiddleware

Expand All @@ -17,6 +17,7 @@

# Constants
DATA_REPO_LOCK = threading.Lock()
GIT_ENABLED = True # disable to pause pushing to git, useful for heavy db operations


class Database:
Expand Down Expand Up @@ -274,50 +275,81 @@ def __enter__(self):
return self.tinydb

def __exit__(self, exc_type, exc_val, exc_tb):
self.sync()
self.lock.release()
try:
self.sync()
finally:
self.lock.release()

def sync(self):
# Only call flush if using CachingMiddleware
if hasattr(self.tinydb.storage, 'flush'):
self.tinydb.storage.flush()

# Git operations - commit and push changes if using git
with DATA_REPO_LOCK:
if self.use_git and self.repo is not None:
try:
# Check for untracked database files and tracked files with changes
status = self.repo.git.status('--porcelain')

# If there are any changes or untracked files
if status:
# Add ALL json files in the directory to ensure we track all databases
json_files = [f for f in os.listdir(self.db_dir) if f.endswith('.json')]
if json_files:
for json_file in json_files:
file_path = os.path.join(self.db_dir, json_file)
self.repo.git.add(file_path)

# Check if we have anything to commit after adding
if self.repo.git.status('--porcelain'):
# Ensure the repository is configured with user identity
self._configure_repo()

# Commit all changes at once with a general message
commit_message = "Update database files"
self.repo.git.commit('-m', commit_message)
print("Committed changes to git data repository")

# Push to remote with credentials
try:
# Ensure we're using the credentials for push
protocol, repo_path = self.repo_url.split("://", 1)
push_url = f"{protocol}://{self.git_user_name}:{self.git_token}@{repo_path}"
self.repo.git.push(push_url, self.repo_branch)
print("Pushed changes to remote git data repository")
except git.exc.GitCommandError as e:
print(f"Failed to push changes: {str(e)}")

except Exception as e:
print(f"Git operation failed: {str(e)}")
traceback.print_exc()
try:
# Flush changes to disk if possible
if self.tinydb and hasattr(self.tinydb.storage, 'flush'):
self.tinydb.storage.flush()

# Close the database to ensure file is available for Git operations
if self.tinydb is not None:
self.tinydb.close()
self.tinydb = None

# Git operations with closed file
with DATA_REPO_LOCK:
if self.use_git and self.repo is not None and GIT_ENABLED:
try:
# Check for untracked database files and tracked files with changes
status = self.repo.git.status('--porcelain')

# If there are any changes or untracked files
if status:
# Add ALL json files in the directory to ensure we track all databases
json_files = [f for f in os.listdir(self.db_dir) if f.endswith('.json')]
if json_files:
for json_file in json_files:
file_path = os.path.join(self.db_dir, json_file)
self.repo.git.add(file_path)

# Check if we have anything to commit after adding
if self.repo.git.status('--porcelain'):
# Ensure the repository is configured with user identity
self._configure_repo()

# Commit all changes at once with a general message
commit_message = "Update database files"
self.repo.git.commit('-m', commit_message)
print("Committed changes to git data repository")

# Push to remote with credentials
try:
# Ensure we're using the credentials for push
protocol, repo_path = self.repo_url.split("://", 1)
push_url = f"{protocol}://{self.git_user_name}:{self.git_token}@{repo_path}"
self.repo.git.push(push_url, self.repo_branch)
print("Pushed changes to remote git data repository")
except git.exc.GitCommandError as e:
print(f"Failed to push changes: {str(e)}")

except Exception as e:
print(f"Git operation failed: {str(e)}")
traceback.print_exc()
finally:
# Ensure database is ready for next use
if self.tinydb is None:
self.tinydb = TinyDB(
self.json_path,
storage=CachingMiddleware(JSONStorage),
indent=4,
)

@staticmethod
def query():
"""
Get the TinyDB Query object for constructing database queries.

This is a helper method to avoid importing the Query class directly
in modules that use the Database class.

Returns
-------
Query
A TinyDB Query object for constructing queries.
"""
return Query()
Loading