Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
b2215ea
Fix SQL injection vulnerabilities and improve database operations
NiXTheDev Dec 26, 2025
cc4102c
Add custom404ImagePath to settings: schema, types, and models
NiXTheDev Dec 26, 2025
0ada39b
Add settings menu and 404 image upload handler
NiXTheDev Dec 26, 2025
ba0bd93
Integrate custom 404 image into entry viewing
NiXTheDev Dec 26, 2025
315fe77
Add testing for custom 404 image settings
NiXTheDev Dec 26, 2025
460c523
Fix linting issues and clean up unused imports
NiXTheDev Dec 26, 2025
af0e3bc
Fix custom API URL configuration for file downloads and API calls
NiXTheDev Dec 26, 2025
8a928a0
Improve user experience with clearer prompts, examples, and help text
NiXTheDev Dec 26, 2025
a16ab51
Update README with configuration instructions
NiXTheDev Dec 26, 2025
48b4a23
Add configurable Telegram API base URL support
NiXTheDev Dec 26, 2025
27b8203
Fix custom API URL configuration for file downloads and API calls
NiXTheDev Dec 26, 2025
e81b20e
Fix database creation by ensuring file exists before table creation
NiXTheDev Dec 26, 2025
15eaa4c
Fix HTML formatting by adding parse_mode to all HTML messages
NiXTheDev Dec 26, 2025
c39595e
Add fallback to official Telegram API for file downloads when custom …
NiXTheDev Dec 26, 2025
9f0f626
Fix file path handling for Telegram file downloads - extract relative…
NiXTheDev Dec 26, 2025
7b38a54
Fix settings initialization - ensure settings record exists before up…
NiXTheDev Dec 26, 2025
f427ee1
Add error resilience to conversations - retry mechanisms, graceful er…
NiXTheDev Dec 26, 2025
0618691
Implement auto-retry functionality with fallback API support and conv…
NiXTheDev Dec 26, 2025
1859672
Adding a way to view journal entries.
CodeCanna Dec 26, 2025
b9e3137
Removed a line saying selfie features arent working.
CodeCanna Dec 26, 2025
41e133f
Fixed reply message to say data instead of entries
CodeCanna Dec 26, 2025
2bda40d
Added view_journal_entries conversation.
CodeCanna Dec 26, 2025
ba50f91
code cleanup
CodeCanna Dec 26, 2025
850e2f9
added /bin to .gitignore to store deno binaries
CodeCanna Dec 26, 2025
d13fa18
Merge upstream changes (dd2d80f-43ebb2e)
NiXTheDev Dec 27, 2025
dfde22e
fix: deno fmt
NiXTheDev Dec 27, 2025
d3c693c
Fix linting errors: remove unused imports and replace any types with …
NiXTheDev Dec 27, 2025
fcc2124
Fix type errors: properly handle nullable database fields and PRAGMA …
NiXTheDev Dec 27, 2025
fb2bcd5
fix: deno fmt\nagain...
NiXTheDev Dec 27, 2025
b29ca2a
Replace console.log with proper logging framework
NiXTheDev Dec 27, 2025
d8aad1b
Consolidate database connections to use withDB helper
NiXTheDev Dec 27, 2025
0f808f8
Refactor: Fix non-null assertions, add validation, remove dead code, …
NiXTheDev Dec 27, 2025
dee3628
Refactor: Standardize error handling patterns
NiXTheDev Dec 27, 2025
018c7cb
Refactor: Optimize database integrity checks and error messages
NiXTheDev Dec 27, 2025
53591ba
Refactor: Improve error messages for better UX
NiXTheDev Dec 27, 2025
414034a
fix: deno lint
NiXTheDev Dec 27, 2025
34f4524
fix: @std/log version pinned to ^0.224.14 instead of ^0.224
NiXTheDev Dec 27, 2025
60411f9
fix: deno fmt
NiXTheDev Dec 27, 2025
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
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ entries. You can also delete entries from this screen. If you are wanting to
stop using Jotbot you can delete your account using /delete_account this will
also delete all of your journal entries!

## Configuration

The bot can be configured using environment variables in a `.env` file:

- `TELEGRAM_BOT_KEY`: Your Telegram bot token (required)
- `TELEGRAM_API_BASE_URL`: Custom Telegram Bot API base URL (optional, defaults
to `https://api.telegram.org`)

## Commands

**/start** - Start the bot, if it's your first time messaging the bot you will
Expand Down
Binary file added assets/404/1680564645_404.jpg
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you forget to add this to .gitignore? :3

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likely, but then we need to gitignore all images

Copy link
Owner

@CodeCanna CodeCanna Dec 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I would think we would ignore all images in the custom 404 image directory because those are dynamic user settings, then we can set the default 404.png in /assets to a more neutral image?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I suppose so, though I we need a separate location for the default

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also I'm not sure if I'm doing something wrong, but the bot won't run on my laptop after merging the changes. I can see all of the tests passed in Github so that's why I think it's something I'm doing. I'm getting

error: Import "@std/dotenv" not a dependency and not in import map from "file:///home/awesomepilot/Documents/programming/deno/jotbot/main.ts"
  hint: If you want to use the JSR package, try running `deno add jsr:@std/dotenv`
    at file:///home/awesomepilot/Documents/programming/deno/jotbot/main.ts:2:22

When I add that dependency I get

error: Uncaught SyntaxError: The requested module '../constants/strings.ts' does not provide an export named 'telegramDownloadUrl'
import { telegramDownloadUrl } from "../constants/strings.ts";

Am I doing something wrong here? To be honest this is the first time someone has contributed to one of my projects like this, so I could be missing something. If all of your tests passed it should run for me, before I started making changes I thought I should ask.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how that happened, but i am already working on a fix

Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions constants/numbers.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const pdfFontSize = 30;
export const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB limit for file uploads
53 changes: 38 additions & 15 deletions constants/strings.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
export const startString: string =
`Hello! Welcome to JotBot. I'm here to help you record your emotions and emotion!`;
export const telegramDownloadUrl =
"https://api.telegram.org/file/bot<token>/<file_path>";
// This will be constructed dynamically using the configured API base URL
export const getTelegramDownloadUrl = (
baseUrl: string,
token: string,
filePath: string,
) => `${baseUrl}/file/bot${token}/${filePath}`;

export const catImagesApiBaseUrl = `https://cataas.com`;
export const quotesApiBaseUrl = `https://zenquotes.io/api/quotes/`;
Expand Down Expand Up @@ -37,22 +41,41 @@ export const helpString: string = `
Jotbot is a telegram bot that can help you to record your thoughts and emotions in directly in the telegram app.

<b><u>How do I use Jotbot?</u></b>
Jotbot is easy to use you have to be "registered" to start recording entries.
Once this is done you can use /new_entry to start recording an entry. You just answer the bot's questions to the best of you ability from there.

After you are finished recording your entry you can view your entry by using /view_entries. This will bring up a menu that let's you scroll through your entries. You can also delete entries from this screen.
If you are wanting to stop using Jotbot you can delete your account using /delete_account this will also delete all of your journal entries!
<b>🚀 Getting Started:</b>
• Send /start to begin registration
• Follow the prompts to create your profile

<b>📝 Creating Entries:</b>
• Use /new_entry to start a new journal entry
• Answer 4 simple questions about your thoughts and emotions
• Each step is clearly labeled with examples

<b>👀 Viewing Entries:</b>
• Use /view_entries to browse your journal
• Navigate with Previous/Next buttons
• Edit or delete entries as needed

<b>⚙️ Settings:</b>
• Use /settings to customize your experience
• Toggle mental health score saving
• Set a custom 404 image for entries without photos

<b>🆘 Need Help?</b>
• Use /delete_account to remove all your data

<b><u>Commands</u></b>
/start - Start the bot, if it's your first time messaging the bot you will be asked if you want to register.
/help - Prints this help string in a message
/new_entry - Start a new entry
/view_entries - Scroll through your entries
/kitties - Open the kitties app! Studies show kitties can help with depression
/delete_account - Delete your accound plus all entries
/🆘 or /sos - Show the crisis help lines

<b>NOTE</b>: The selfie features aren't working right now.
/start - Register or access your account
/help - Show this help message
/new_entry - Create a new journal entry (4 simple steps)
/view_entries - Browse and manage your entries
/settings - Customize your bot experience
/kitties - View cute cats for stress relief
/am_i_depressed - Take a depression assessment (PHQ-9)
/am_i_anxious - Take an anxiety assessment (GAD-7)
/snapshot - View your mental health summary
/delete_account - Permanently delete all your data
/🆘 or /sos - Access crisis support resources
`;

export enum Emotions {
Expand Down
39 changes: 31 additions & 8 deletions db/migration.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { PathLike } from "node:fs";
import { DatabaseSync } from "node:sqlite";
import { sqlFilePath } from "../constants/paths.ts";
import { logger } from "../utils/logger.ts";

export function createEntryTable(dbFile: PathLike) {
try {
Expand All @@ -11,7 +12,7 @@ export function createEntryTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`Failed to create entry_db table: ${err}`);
logger.error(`Failed to create entry_db table: ${err}`);
}
}

Expand All @@ -25,7 +26,7 @@ export function createGadScoreTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`There was a a problem create the user_db table: ${err}`);
logger.error(`Failed to create gad_score_db table: ${err}`);
}
}

Expand All @@ -39,7 +40,7 @@ export function createPhqScoreTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`There was a a problem create the user_db table: ${err}`);
logger.error(`Failed to create phq_score_db table: ${err}`);
}
}

Expand All @@ -53,7 +54,7 @@ export function createUserTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`There was a a problem create the user_db table: ${err}`);
logger.error(`Failed to create user_db table: ${err}`);
}
}

Expand All @@ -67,7 +68,7 @@ export function createSettingsTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`Failed to create settings table: ${err}`);
logger.error(`Failed to create settings_db table: ${err}`);
}
}

Expand All @@ -81,7 +82,7 @@ export function createJournalTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`Failed to create settings table: ${err}`);
logger.error(`Failed to create journal_db table: ${err}`);
}
}

Expand All @@ -94,7 +95,7 @@ export function createJournalEntryPhotosTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`Failed to create settings table: ${err}`);
logger.error(`Failed to create photo_db table: ${err}`);
}
}

Expand All @@ -108,6 +109,28 @@ export function createVoiceRecordingTable(dbFile: PathLike) {
db.prepare(query).run();
db.close();
} catch (err) {
console.error(`Failed to create settings table: ${err}`);
logger.error(`Failed to create voice_recording_db table: ${err}`);
}
}

export function addCustom404Column(dbFile: PathLike) {
try {
const db = new DatabaseSync(dbFile);
db.exec("PRAGMA foreign_keys = ON;");
// Check if column exists to avoid errors
const columns = db.prepare("PRAGMA table_info(settings_db);").all() as {
name: string;
}[];
const hasColumn = columns.some((col) => col.name === "custom404ImagePath");
if (!hasColumn) {
db.prepare(`
ALTER TABLE settings_db
ADD COLUMN custom404ImagePath TEXT DEFAULT NULL;
`).run();
logger.info("Added custom404ImagePath column to settings_db");
}
db.close();
} catch (err) {
logger.error(`Failed to add custom404ImagePath column: ${err}`);
}
}
1 change: 1 addition & 0 deletions db/sql/create_settings_table.sql
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ CREATE TABLE IF NOT EXISTS settings_db (
id INTEGER PRIMARY KEY AUTOINCREMENT,
userId INTEGER,
storeMentalHealthInfo INTEGER DEFAULT 0,
custom404ImagePath TEXT DEFAULT NULL,
FOREIGN KEY (userId) REFERENCES user_db(telegramId) ON DELETE CASCADE
);
2 changes: 1 addition & 1 deletion db/sql/misc/get_latest_entry_id.sql
Original file line number Diff line number Diff line change
@@ -1 +1 @@
SELECT seq FROM sqlite_sequence WHERE name='<TABLE_NAME>';
SELECT MAX(id) as max_id FROM <TABLE_NAME>;
3 changes: 2 additions & 1 deletion deno.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@
},
"imports": {
"@grammyjs/commands": "npm:@grammyjs/commands@^1.2.0",
"@grammyjs/conversations": "npm:@grammyjs/conversations@^2.1.1",
"@grammyjs/conversations": "npm:@grammyjs/conversations@^1.1.1",
"@grammyjs/files": "npm:@grammyjs/files@^1.2.0",
"@std/assert": "jsr:@std/assert@^1.0.16",
"@std/log": "jsr:@std/log@^0.224.14",
"grammy": "npm:grammy@^1.38.4"
}
}
33 changes: 33 additions & 0 deletions deno.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 10 additions & 5 deletions handlers/delete_account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ import { Conversation } from "@grammyjs/conversations";
import { deleteAccountConfirmKeyboard } from "../utils/keyboards.ts";
import { deleteUser } from "../models/user.ts";
import { dbFile } from "../constants/paths.ts";
import { logger } from "../utils/logger.ts";

export async function delete_account(conversation: Conversation, ctx: Context) {
if (!ctx.from) {
await ctx.reply("Error: Unable to identify user.");
return;
}
try {
await ctx.reply(
`⚠️ Are you sure you want to <b><u>delete</u></b> your account <b>along with all of your data</b>? ⚠️`,
Expand All @@ -17,21 +22,21 @@ export async function delete_account(conversation: Conversation, ctx: Context) {
]);

if (deleteAccountCtx.callbackQuery.data === "delete-account-yes") {
await conversation.external(() => deleteUser(ctx.from?.id!, dbFile));
await conversation.external(() => deleteUser(ctx.from.id, dbFile));
} else if (deleteAccountCtx.callbackQuery.data === "delete-account-no") {
conversation.halt();
return await deleteAccountCtx.editMessageText("No changes made!");
}
await conversation.halt();
return await ctx.editMessageText(
`Okay ${ctx.from?.username} your account has been terminated along with all of your entries. Thanks for trying Jotbot!`,
`Okay ${ctx.from.username} your account has been terminated along with all of your entries. Thanks for trying Jotbot!`,
);
} catch (err) {
console.log(
`Failed to delete user ${ctx.from?.username}: ${err}`,
logger.error(
`Failed to delete user ${ctx.from.username}: ${err}`,
);
return await ctx.editMessageText(
`Failed to delete user ${ctx.from?.username}: ${err}`,
`Failed to delete user ${ctx.from.username}: ${err}`,
);
}
}
Loading