Skip to content

Commit

Permalink
Fix basic voting logic and add types (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
gpont authored Jul 25, 2024
1 parent b34a057 commit 4c618f2
Show file tree
Hide file tree
Showing 11 changed files with 137 additions and 57 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

[![CI](https://github.com/gpont/watch_together/actions/workflows/ci.yml/badge.svg)](https://github.com/gpont/watch_together/actions/workflows/ci.yml)

![preview-image](./docs/preview-image.png)

Telegram bot for selecting movies to watch together.

## Bot commands
Expand Down
Binary file added docs/preview-image.png
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 src/consts.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export const DATABASE_FILENAME = './db/database.db';
export const KINOPOISK_URL = 'https://www.kinopoisk.ru/index.php?kp_query=';
export const IMDB_URL = 'https://www.imdb.com/find/?q=';
60 changes: 38 additions & 22 deletions src/controllers/bot.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ jest.mock('node-telegram-bot-api');
jest.mock('../consts.ts', () => ({
DATABASE_FILENAME: './test_database.db',
KINOPOISK_URL: 'https://www.kinopoisk.ru/index.php?kp_query=',
IMDB_URL: 'https://www.imdb.com/find/?q=',
}));

describe('Bot Commands', () => {
Expand Down Expand Up @@ -68,7 +69,7 @@ describe('Bot Commands', () => {
msg.chat.id,
expect.stringContaining('Привет!'),
);
expect(user.group_id).toBe(msg.chat.id);
expect(user?.group_id).toBe(msg.chat.id);
});

it('help command should send help message', async () => {
Expand Down Expand Up @@ -115,7 +116,8 @@ describe('Bot Commands', () => {
text: `/join_group ${chatId}`,
from: { id: 1 },
} as unknown as TelegramBot.Message;
await addUserToGroup(msg.chat.id, user.id);
expect(user).not.toBeUndefined();
await addUserToGroup(msg.chat.id, user?.id ?? 0);

await emitMsg(msg);

Expand All @@ -135,15 +137,17 @@ describe('Bot Commands', () => {
} as unknown as TelegramBot.Message;
await createGroup(String(msg.chat.id));
const user = await createUser(msg.chat.id);
await addUserToGroup(msg.chat.id, user.id);
expect(user).not.toBeUndefined();
await addUserToGroup(msg.chat.id, user?.id ?? 0);

await emitMsg(msg);

const movie = await suggestMovie(
'Inception',
user.id,
user?.id ?? 0,
msg.chat.id,
'https://www.kinopoisk.ru/index.php?kp_query=Inception',
'https://www.imdb.com/find/?q=Inception',
);
expect(movie).not.toBeNull();
expect(sendMessage).toHaveBeenCalledWith(
Expand All @@ -160,22 +164,25 @@ describe('Bot Commands', () => {
} as unknown as TelegramBot.Message;
await createGroup(String(msg.chat.id));
const user = await createUser(msg.chat.id);
await addUserToGroup(msg.chat.id, user.id);
expect(user).not.toBeUndefined();
await addUserToGroup(msg.chat.id, user?.id ?? 0);
const insertedMovie = await suggestMovie(
'Inception',
user.id,
user?.id ?? 0,
msg.chat.id,
'https://www.kinopoisk.ru/index.php?kp_query=Inception',
'https://www.imdb.com/find/?q=Inception',
);
expect(insertedMovie).not.toBeUndefined();

await emitMsg({
...msg,
text: `/vote ${insertedMovie.id}`,
text: `/vote ${insertedMovie?.id}`,
});

const movie = await findMovieById(insertedMovie.id, msg.chat.id);
const movie = await findMovieById(insertedMovie?.id ?? 0, msg.chat.id);

expect(movie.votes).toBe(1);
expect(movie?.votes).toBe(1);
expect(sendMessage).toHaveBeenCalledWith(
msg.chat.id,
expect.stringContaining('Вы проголосовали за фильм!'),
Expand All @@ -190,18 +197,22 @@ describe('Bot Commands', () => {
} as unknown as TelegramBot.Message;
await createGroup(String(msg.chat.id));
const user = await createUser(msg.chat.id);
await addUserToGroup(msg.chat.id, user.id);
expect(user).not.toBeUndefined();
await addUserToGroup(msg.chat.id, user?.id ?? 0);
const movie = await suggestMovie(
'Inception',
user.id,
user?.id ?? 0,
msg.chat.id,
'https://www.kinopoisk.ru/index.php?kp_query=Inception',
'https://www.imdb.com/find/?q=Inception',
);
expect(movie).not.toBeUndefined();

await emitMsg(msg);
expect(sendMessage).toHaveBeenCalledWith(
msg.chat.id,
expect.stringContaining(movie.name),
expect.stringContaining(movie?.name ?? 'Inception'),
expect.objectContaining({}),
);
});

Expand All @@ -210,25 +221,27 @@ describe('Bot Commands', () => {
const chatId = 129;
await createGroup(String(chatId));
const user = await createUser(chatId);
expect(user).not.toBeUndefined();
const msg = {
chat: { id: chatId },
text: '/veto 1',
from: { id: user.id },
from: { id: user?.id },
} as unknown as TelegramBot.Message;
await addUserToGroup(msg.chat.id, user.id);
await addUserToGroup(msg.chat.id, user?.id ?? 0);
const movie = await suggestMovie(
'Inception',
user.id,
user?.id ?? 0,
msg.chat.id,
'https://www.kinopoisk.ru/index.php?kp_query=Inception',
'https://www.imdb.com/find/?q=Inception',
);

await emitMsg({
...msg,
text: `/veto ${movie.id}`,
text: `/veto ${movie?.id}`,
});

expect(movie).not.toBeNull();
expect(movie).not.toBeUndefined();
expect(sendMessage).toHaveBeenCalledWith(
msg.chat.id,
expect.stringContaining('Вы не можете наложить вето на свой же фильм.'),
Expand All @@ -241,23 +254,26 @@ describe('Bot Commands', () => {
await createGroup(String(chatId));
const user1 = await createUser(chatId);
const user2 = await createUser(chatId);
expect(user1).not.toBeUndefined();
expect(user2).not.toBeUndefined();
const msg = {
chat: { id: chatId },
text: '/veto 1',
from: { id: user2.id },
from: { id: user2?.id ?? 0 },
} as unknown as TelegramBot.Message;
await addUserToGroup(msg.chat.id, user1.id);
await addUserToGroup(msg.chat.id, user2.id);
await addUserToGroup(msg.chat.id, user1?.id ?? 0);
await addUserToGroup(msg.chat.id, user2?.id ?? 0);
const movie = await suggestMovie(
'Inception',
user1.id,
user1?.id ?? 0,
msg.chat.id,
'https://www.kinopoisk.ru/index.php?kp_query=Inception',
'https://www.imdb.com/find/?q=Inception',
);

await emitMsg({
...msg,
text: `/veto ${movie.id}`,
text: `/veto ${movie?.id ?? 0}`,
});

expect(movie).not.toBeNull();
Expand Down
48 changes: 33 additions & 15 deletions src/controllers/bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
markMovieAsWatched,
} from '../models/moviesModel';
import texts from '../texts.json';
import { getKinopoiskUrl } from './helpers';
import { getImdbUrl, getKinopoiskUrl, getMovieDescription } from './helpers';

type THandler = (
bot: TelegramBot,
Expand Down Expand Up @@ -85,13 +85,31 @@ export const botHandlers: [RegExp, THandler][] = [
const user = await createUser(chatId);
const groupUser = await findGroupByCode(String(chatId));

if (groupUser) {
const link = getKinopoiskUrl(movieName);
await suggestMovie(movieName, user.id, groupUser.id, link);
bot.sendMessage(chatId, `${texts.movie_suggested} "${movieName}"`);
} else {
if (!user) {
bot.sendMessage(chatId, texts.user_not_found);
return;
}

if (!groupUser) {
bot.sendMessage(chatId, texts.not_in_group);
return;
}

const movie = await suggestMovie(
movieName,
user.id,
groupUser.id,
getKinopoiskUrl(movieName),
getImdbUrl(movieName),
);
if (!movie) {
bot.sendMessage(chatId, texts.movie_not_added);
return;
}
bot.sendMessage(
chatId,
`${texts.movie_suggested}:\n${getMovieDescription(movie)}"`,
);
},
],
[
Expand All @@ -104,7 +122,7 @@ export const botHandlers: [RegExp, THandler][] = [
return;
}

const movieId = match.slice(1).join(' ');
const movieId = parseInt(match.slice(1).join(''), 10);

const movie = await findMovieById(movieId, chatId);

Expand All @@ -122,12 +140,12 @@ export const botHandlers: [RegExp, THandler][] = [
const chatId = msg.chat.id;
const movies = await listMovies();

if (movies.length > 0) {
let movieList = texts.movie_list;
for (const movie of movies) {
movieList += `${movie.id}. ${movie.name} (голоса: ${movie.votes}) - ${movie.link}\n`;
}
bot.sendMessage(chatId, movieList);
if (!!movies && movies.length > 0) {
const movieList =
texts.movie_list + movies.map(getMovieDescription).join('');
bot.sendMessage(chatId, movieList, {
disable_web_page_preview: true,
});
} else {
bot.sendMessage(chatId, texts.movie_list_empty);
}
Expand All @@ -143,7 +161,7 @@ export const botHandlers: [RegExp, THandler][] = [
return;
}

const movieId = match.slice(1).join(' ');
const movieId = parseInt(match.slice(1).join(''), 10);

const movie = await findMovieById(movieId, chatId);

Expand Down Expand Up @@ -171,7 +189,7 @@ export const botHandlers: [RegExp, THandler][] = [
return;
}

const movieId = match.slice(1).join(' ');
const movieId = parseInt(match.slice(1).join(''), 10);

const movie = await findMovieById(movieId, chatId);

Expand Down
10 changes: 9 additions & 1 deletion src/controllers/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { KINOPOISK_URL } from '../consts';
import { IMDB_URL, KINOPOISK_URL } from '../consts';
import { IMovie } from '../models/moviesTypes';

export const getKinopoiskUrl = (movieId: string) =>
`${KINOPOISK_URL}${encodeURIComponent(movieId)}`;

export const getImdbUrl = (movieId: string) =>
`${IMDB_URL}${encodeURIComponent(movieId)}`;

export const getMovieDescription = (movie: IMovie) =>
`${movie.id}. ${movie.name} (__голоса: ${movie.votes}__) /vote ${movie.id}\n` +
`[Кинопоиск](${movie.kinopoisk_link}) | [IMDB](${movie.imdb_link})\n`;
5 changes: 3 additions & 2 deletions src/models/database.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,13 @@ describe('Database Tests', () => {
it('should insert and retrieve movie', async () => {
const db = await openDb();
await db.run(
`INSERT INTO movies (name, suggested_by, group_id, link) VALUES ('Test Movie', 1, 1, 'http://example.com')`,
`INSERT INTO movies (name, suggested_by, group_id, kinopoisk_link, imdb_link) VALUES ('Test Movie', 1, 1, 'http://example1.com', 'http://example2.com')`,
);
const movie = await db.get(
`SELECT * FROM movies WHERE name = 'Test Movie'`,
);
expect(movie.name).toBe('Test Movie');
expect(movie.link).toBe('http://example.com');
expect(movie.kinopoisk_link).toBe('http://example1.com');
expect(movie.imdb_link).toBe('http://example2.com');
});
});
3 changes: 2 additions & 1 deletion src/models/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export async function initializeDb() {
name TEXT,
suggested_by INTEGER,
votes INTEGER DEFAULT 0,
link TEXT,
kinopoisk_link TEXT,
imdb_link TEXT,
group_id INTEGER,
FOREIGN KEY (suggested_by) REFERENCES users(id),
FOREIGN KEY (group_id) REFERENCES groups(id)
Expand Down
Loading

0 comments on commit 4c618f2

Please sign in to comment.