GithubHelp home page GithubHelp logo

qelphybox / hakeshonassybot Goto Github PK

View Code? Open in Web Editor NEW
11.0 11.0 5.0 3.08 MB

Fun telegram bot

License: MIT License

JavaScript 92.07% Dockerfile 0.63% Makefile 1.98% Sass 0.02% HTML 0.73% Shell 4.53% Procfile 0.04%
achievements fun funny hacktoberfest russian statistics telegram telegram-bot

hakeshonassybot's People

Contributors

dependabot-preview[bot] avatar dependabot[bot] avatar johnny32id avatar marselmustafin avatar qelphybox avatar smthingwicked avatar svyborov avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

hakeshonassybot's Issues

Ачивка безработный определяется не правильно

Kirill Bobykin, [15.05.20 20:10]
завел бота

Kirill Bobykin, [15.05.20 20:10]
/stats

Hake Shonassy, [15.05.20 20:10]
Сообщений за последние 24 часа: @kirillbobykin (1)
Сообщений за последний час: @kirillbobykin (1)
@kirillbobykin - безработный

Он определил, что я безработный, хотя у меня одно сообщение в пятницу 20:10 мск (17:10 UTC). Я думаю стоит определять не по utc, а по мск все таки, сбивает с толку ребят.

Ачивка "Юморист"

Идея ачивки - человек с наибольшим количеством реакций, обозначающих смех. В реакции могут входить:

  1. Текст содержащий последовательности вида "ахахахах"
  2. Эмодзи обозначающие смех
  3. Аббревиатуры означающие смех - лол, кек

В пункте 1 следует учесть возможные опечатки, раскладку (f[f[f[f), регистр, производные формы (хех, хихи)

Выделить слой actions

У нас постоянно какие-то баги после выкатки, в итоге мы всегда выкатываемся в несколько раундов, чтобы это избежать нам нам нужно как можно более верхнеуровневое тестирование.
Приемлемый уровень, имхо, функции которыми мы реагируем на события в телеграме, нужно выделить в модуль и плотно закрыть его тестами.

Реализовать GET /api/stupid_achievments

GET /api/stupid_achievments - отдает все поддерживаемые глупые ачивки.

  1. Валидируем данные сессии (https://core.telegram.org/widgets/login#checking-authorization)
  2. Если валидно отдаем:
    200 OK
    {   
      "status": "ok",
      "stupid_achievments": [
        { "title": "Количество сообщений", "name": "messages_count" },
        { "title": "Безработный", "name": "workless_user" },
        { "title": "Поставщик контента", "name": "content_supplier" },
        { "title": "Худший юзер чата", "name": "worst_chat_user" },
        { "title": "Стикерпакер", "name": "stickerpacker" },
        { "title": "Пропавший без вести", "name": "maybe_died" },
        { "title": "Дудь", "name": "dud" },
        { "title": "Философ", "name": "philosopher" }
      ] 
    }
  3. Если невалидно, отдаем
    403 Forbidden
    {
      "status": "forbidden"
    }

Уборка тестов

  • Оставьте только actions тесты, тесты отдельных статистик не нужны, удалите их и все что с ними связано.
  • actions тесты нужно раскидать по файлам, в зависимости от тестируемого функционала. Например, файл с тестированием статистики безработного, файл с тестированием статистики стикерпакера и тд.

Некоторые юзернэймы не определяются

Hake Shonassy, [16.05.20 03:49]
Сообщений за последние 24 часа: @Johnny3_2 (27), @Andrusishin (12), @null (10), @kirillbobykin (8), @null (7), @Fulgrim_94 (5), @null (4), @null (3), @NobodyKnowsa (3), @null (2)
Сообщений за последний час: 
@Johnny3_2 - безработный

Нужно изменить алгоритм определения имени

Уточнение ачивок

Сейчас вот так:

Сообщений за последние 24 часа: Вадим (21), Dima (19), Евгений Слиж (17), Alexander Malykh (17), Kirill Bobykin (14), M B (14), Alexey Danilov (12), Melnikov Leonid (11), Дмитрий Старов (4), Alexandr Legotin (3)
Сообщений за последний час: Вадим (2), Kirill Bobykin (2), Alexey Danilov (1)
Alexey Danilov - безработный
Alexey Danilov - поставщик контента
Дмитрий Старов - худший юзер чата
Вадим - стикерпакер

Нужно чтобы было вот так:

Сообщений за последние 24 часа: Вадим (21), Dima (19), Евгений Слиж (17), Alexander Malykh (17), Kirill Bobykin (14), M B (14), Alexey Danilov (12), Melnikov Leonid (11), Дмитрий Старов (4), Alexandr Legotin (3)
Сообщений за последний час: Вадим (2), Kirill Bobykin (2), Alexey Danilov (1)
Alexey Danilov - безработный (123 сообщения в рабочее время за неделю)
Alexey Danilov - поставщик контента (3 картинки, 1 видео)
Дмитрий Старов - худший юзер чата (послал 10 голосовых)
Вадим - стикерпакер (юзает 4 стикерпака)

После ачивок должно появиться уточненение по стату

Доработать DBClient

// src/dbClient.js
class DBClient {
//....
}
const dbClient = new DBClient();
Object.freeze(dbClient);
export default dbClient;

Сделать migrate.js идемпотентным

Сейчас при выкатке мы вызываем src/migrate.js
Он падает с логом:

> [email protected] migrate /app
> node src/migrate.js
Start migration...
(node:42) DeprecationWarning: current Server Discovery and Monitoring engine is deprecated, and will be removed in a future version. To use the new Server Discover and Monitoring engine, pass option { useUnifiedTopology: true } to the MongoClient constructor.
migration failed with error:  MongoError: a collection 'hakeshonassydb_production.messages' already exists
    at Connection.<anonymous> (/app/node_modules/mongodb/lib/core/connection/pool.js:451:61)
    at Connection.emit (events.js:314:20)
    at processMessage (/app/node_modules/mongodb/lib/core/connection/connection.js:452:10)
    at TLSSocket.<anonymous> (/app/node_modules/mongodb/lib/core/connection/connection.js:621:15)
    at TLSSocket.emit (events.js:314:20)
    at addChunk (_stream_readable.js:298:12)
    at readableAddChunk (_stream_readable.js:273:9)
    at TLSSocket.Readable.push (_stream_readable.js:214:10)
    at TLSWrap.onStreamRead (internal/stream_base_commons.js:188:23) {
  operationTime: Timestamp { _bsontype: 'Timestamp', low_: 8, high_: 1603202328 },
  ok: 0,
  code: 48,
  codeName: 'NamespaceExists',
  '$clusterTime': {
    clusterTime: Timestamp { _bsontype: 'Timestamp', low_: 8, high_: 1603202328 },
    signature: { hash: [Binary], keyId: [Long] }
  }
}
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! [email protected] migrate: `node src/migrate.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the [email protected] migrate script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR!     /app/.npm/_logs/2020-10-20T13_58_49_265Z-debug.log

Нужно сделать выполнение идемпотентным, то есть если у нас нет коллекции и индексов, создаём их, если есть то не создаём но и не падаем.

Ачивка "безработный" не опеределяется

Статистика безработный (workless) не определяется, в чате есть переписки на неделе, но нет ачивки при вызове /stats

Kirill Bobykin, [24.05.20 22:43]
/stats

Hake Shonassy, [24.05.20 22:43]
Сообщений за последние 24 часа: Alexey Danilov (12), Вадим (11), Дмитрий Старов (7), Евгений Слиж (6), Kirill Bobykin (5), Alexander Malykh (5), Ivan Slotin (3), M B (2)
Сообщений за последний час: Kirill Bobykin (5), Alexander Malykh (4), Ivan Slotin (2), Евгений Слиж (1)

Поправить уточнение ачивок

Сообщений за последние 24 часа: Alexey Danilov (93), Вадим (77), Melnikov Leonid (48), Kirill Bobykin (43), M B (35), Евгений Слиж (34), Alexander Malykh (23), Dima (21), Дмитрий Старов (7), Theodor (6), Ivan Slotin (5), Vlad Malikh (2)
Сообщений за последний час: Alexey Danilov (4), M B (2), Евгений Слиж (1)
Alexey Danilov - безработный (614 сообщения в рабочее время за неделю)
Alexey Danilov - поставщик контента (картинок - 10, видео - 0)
Дмитрий Старов - худший юзер чата (послал 4 голосовых)
Вадим - стикерпакер (юзает 26 стикерпака)
Roma Kuhtin - наверное помер
  • Roma Kuhtin - наверное помер (писал 23 дня назад)
  • просклонять существительные после чисел, вот есть норм алгоритм https://github.com/rtivital/proschet

Обнулять счётчик ачивок

Описание ошибки
На данный момент часть ачивок редко активируются и при выводе /stats можно месяцами видеть одного и того же человека под определённой ачивкой.

Предлагаю в вывод /stats показывать только те ачивки, которые активировались в промежутке последних 7 дней.

Доработать ачивку "Дудь"

Сделать выборку за последние 7 дней. Как в безработном только не вычитая выходные и нерабочие часы. То есть если сегодня 26 августва(среда), то собрать надо с 20 августа (прошлый четверг) по сегодня 26е.

Реализовать страницу логина

Страница логина: https://www.figma.com/file/cJUtYaG9QxcVZB9fFntQtt/Hake-Shonassy?node-id=17%3A210
Страница должна быть доступна только незалогиненым пользователям в корне сайта (путь /, например http://localhost:3000/). Залогиненых пользователей должно отправлять на /stupid_achievements
Определить, что юзер залогинен можно по сессии в cookies: в поле hake_session будет json c полями id, first_name, last_name, username, photo_url, auth_date, hash. Если поле пусто - юзер не залогинен.
На страницу нужно добавить телеграм логин виджет в который имя бота и коллбэк будет подаваться переменной, пример

<script async src="https://telegram.org/js/telegram-widget.js?14" data-telegram-login={ bot_login } data-size="large" data-auth-url={ auth_callback_url }></script>

PS: Подключая роутер использйте BrowserRouter

Оптимизация статистики

Сейчас статистика очень медленная, это от неправильно организации данных и не правильного сбора статистики.
В этом таске нужно:

  • собрать данные о нужных метриках для статистики
  • организовать в бд (postresql) их хранение
  • имплементировать метод сбора метрик
  • перегнать старые данные в эти метрики, для сохранения существующего стата.

Каждый пункт зависит от результатов предыдущего. Какие данные и как собирать обсудим лично и зафиксируем в комментариях.

План:

  1. добавить pg в dev окружение
  2. организовать миграции для такой схемы
    message_metrics
    -------
    id:serial
    tg_id:int
    timestamp:datetime
    users_chats_id:int
    photoCount:int
    videoCount:int
    questionCount: int
    stickerSetName:string
    textLength:int
    voiceCount:int
    lolReplyForUser:int
    
    users
    -----
    id:serial
    tg_id:int
    first_name:string
    last_name:string
    
    chats
    -----
    id:serial
    tg_id:int
    name:string
    
    users_chats
    -----------
    id:serial
    user_id:int
    chat_id:int
    
  3. Организовать запись в бд этих данных
    Достаем метрики примерно так и записываем в бд
    const fetchMetrics = (message) => {
      return {
        user: {
          tg_id: 123,
          first_name: 'asdf',
          last_name: 'asdfsadf'
        },
        chat_id: {
          tg_id: 123,
          name: 123,
        },
        message_metrics: {
          tg_id: 123,
          photoCount: 0, // определяем наличие фото в сообщении как в contentSuppier
          videoCount: 0, // определяем наличие видео в сообщении как в contentSuppier
          questionCount: 0, // если в тексте есть вопрос, ставим 1 (осторожно urlы)
          stickerSetName: 0,
          textLength: 0,
          voiceCount: 0, // ставим 1 если сообщение это голосуха
          lolReplyForUser: 2, // humoristStat ищем в сообщениии реакцию смеха, и если она есть записываем сюда id юзера из реплая reply_to_message.from.id
        }
      };
    };
  4. написать скрипт перегона данных из mongo в pg:
    кусками достаем сообщения из монги маппим их в метрики, целый батч инсертим в pg

Заметки:
https://www.lightbase.io/freeforlife/ - 500 мб бесплатно

Отформатировать вывод

В выводе /stats, выделить заголовки строк жирным, названия стата и владельца ачивки
Например:


Сообщений за последние 24 часа: Aram Azbekian (52), Kirill Bobykin (25), Leonid Markizov (22), Katia Nokhrina (15), Ki Rill (15), 👨🏼‍💻 Stan Saal (13), Elizaveta Flisyuk (2), Сергей (2), Ilya Kovalenko (1)
Сообщений за последний час: Aram Azbekian (31), Kirill Bobykin (21), Leonid Markizov (17), Katia Nokhrina (6)
👨🏼‍💻 Stan Saal - безработный
Aram Azbekian - поставщик контента
Ki Rill - худший юзер чата

Доработать ачивку "поставщик контента"

Пользователи иногда отправляют картинки из vk или видео с youtube. Если ссылка имеет расширение .jpg или .png или это ссылка на ютуб (короткая или длинная неважно) учитывать в ачивке

Реализовать GET /api/chats/:id/stupid_achievments/:name

GET /api/chats/:id/stupid_achievments/:name?range_type=24h - Возвращает статистку юзера

Параметры могут быть:

  • ?range_type=24h - отдать стат за последние 24 часа (Дефолтный range_type)
  • ?range_type=week - отдать за неделю (аналогично ачивке недельного философа)
  • ?range_type=month - отдать стат за последний месяц (аналогично ачивке недельного философа только за месяц, то есть находим первый день месяца и пляшем от него)
  • ?range_type=custom&range=2020-01-04_2020-01-06 - отдаем с начала дня старта периода, до конца дня конца периода включительно. Период надо провалидировать начало не должно быть позже конца.

Олгоритм:

  1. Валидируем данные сессии (https://core.telegram.org/widgets/login#checking-authorization)
  2. Валидируем параметры range_type один из (24h, week, month, custom), и если custom то надо проверить что есть range и что он правильный
  3. Получить данные для ответа:
    1. В сессии у нас есть данные id, first_name, last_name, username, photo_url, auth_date, hash забираем id. Это id телеграм юзера.
    2. Нужно проверить что юзер состоит в чате из запроса. Если нет, валим 404 ошибку.
    3. Если все ок, данные по чату собираем, формируем ответ.

Ответы:

  1. Если все валидно:
    200 OK
    {
      "status": "ok",
      "stupid_achievment": {
        "name": "messages_count",
        "data": [
          { "username": "Alexey Miroslavich", "value": 132 },
          { "username": "Dani Hood", "value": 121 }
        ]
      } 
    }
  2. Если chats/:id или stupid_achievments/:name несуществует или юзер не состоит в таком чате, отдаем
    404 Not Found
    {
      "status": "error",
      "message": "Can not find chat '12332'"
    }
    или
    {
      "status": "error",
      "message": "Can not find stupid achievment 'asdf'"
    }
  3. Если параметры не валидны, отдаем
    Если например ?range_type=asdf&range=2020-01-06_2020-01-04
    400 Bad request
    {
      "status": "error",
      "message": "Wrong 'range_type=asdf', please try one of 24h, week, month, custom"
    }
    Или если ?range_type=custom&range=2020-01-06_2020-01-04
    400 Bad request
    {
      "status": "error",
      "message": "Wrong 'range=2020-01-06_2020-01-04', the beginning must be no later than the end"
    }
  4. Если сессия невалидна, отдаем
    403 Forbidden
    {
      "status": "forbidden",
      "message": "Session is invalid, please sign in"
    }
    

Удалить shelf/jest-mongodb из проекта

У нас есть пакет @shelf/jest-mongodb. Он нужен чтобы при тестах установить mongodb и подключиться к ней, без монги бот не функционирует.
Тесты мы гоняем:

  1. у себя на компе - в докере у нас уже есть монга, и нам не надо ее еще раз себе устанавливать.
  2. в ci - можно запустить монгу как сервис, поэтому мы можем себе не ставить монгу этим пакетом

Пожалуйста, удалите этот пакет, перепишите job в github action через сервис.

[Ошибка] Исправить ошибку в ачивке "наверное помер"

Описание ошибки

Неправильное значение в дате, написано "1 день назад"

Как повторить ошибку
Шаги с конкретными действиями:

  1. Зайти в чат с ботом
  2. Отправить любое сообщение
  3. Выполнить /stats
  4. Видим ошибку

Ожидаемое поведение
Написано "менее одного дня назад"

Скрины
image

Обновить README.md

В README.md не актуальный пример использования бота. Пожалуйста, сделайте gif c использованием бота
В gif должно быть использование команды /stats и ответ бота со всеми ачивками.

Добавить отказ от претензий

Нужно написать отказ от претензий, в котором обозначается, что хоть мы и не передаем данные 3м лицам, но тем не менее не можем гарантировать приватность сообщений и использовать бота нужно на свой страх и риск.

[Ошибка]

Описание ошибки
При нескольких запусках тестов падали рандомные тесты с ошибкой BulkWriteError: E11000 duplicate key error collection: hakeshonassydb_test.messages index: id dup key: { _id: "5eb83823761f4f001183e8c1" }

Как повторить ошибку
Шаги с конкретными действиями:

  1. Запускать make test несколько раз
  2. Иногда выскакивает ошибка

Ожидаемое поведение
Стабильное прохождение всех тестов.

Заполни поля

  • ОС: Ubuntu 20.04.1

Реализовать страницу глупых ачивок

Дизайн страницы: https://www.figma.com/file/cJUtYaG9QxcVZB9fFntQtt/Hake-Shonassy?node-id=2%3A2
Страница должна быть доступна по пути /stupid_achievements, только залогиненым пользователям. У залогиненых пользователей есть сессия в cookies: в поле hake_session есть json c полями id, first_name, last_name, username, photo_url, auth_date, hash. Если поле пусто - юзер не залогинен.

Запросы для страницы

Данные для страницы получаем по ajax.

  1. Дропдаун чатов, запрос описан здесь
  2. Дропдаун ачивок, запрос описан здесь
  3. Данные статистики, запрос описан здесь
  4. Кнопка выход, 2й запрос отсюда

Добавить ачивку "наверное помер"

Присваивается юзеру чата, который писал последний раз давнее всех
То есть находим последнее сообщение каждого юзера, затем выбираем самое ранее сообщение

Реализовать авторизацию на бэке

Нужно написать авторизацию через телеграм, тут вся полезная инфа https://core.telegram.org/widgets/login

Авторизация через callback поэтому на бэке нужно реализовать пару методов:

  1. GET /auth/callback?id=asdf&first_name=asdf&last_name=asdf&username=asdf&photo_url=asdf&auth_date=asdfhash=asdf
    1. Провалидировать запрос (https://core.telegram.org/widgets/login#checking-authorization)
    2. Если запрос валиден: Сохранить данные юзера в сессию (в куки) и редирект на Home page (304) /?auth=success
    3. Если запрос невалиден: Редирект на Home page (304) /?auth=fail
  2. GET /auth/logout
    1. Удалить из сессии данные юзера
    2. Вернуть 200 { "message": "Bye!" }

Можно сделать тестовые welcome page и home page чтобы можно было протыкать весь процесс.

Добавить ачивку "Философ чата"

Философ чата (chatPhilosopher) — имеет самую большую медиану длины сообщения в чате. То есть считаем каждодому юзеру чата медиану, то у кого это самое большое число - философ.
Пример алгоритма медианы: https://repl.it/@qelphybox/jsmedianexample
Круто будет сделать одним агрегационным запросом в бд.

Алгоритм:
Каждому юзеру считаем длину каждого его сообщения от начала времен (значит нижняя граница не нужна, считаем по всем сообщениям). Для каждого юзера будет как бы массив длин сообщений { user1: [5,6,7,8,9, ...], ...}
Затем каждому юзеру находим медиану этого массива: получится например { user1: 9, ... }
Находим юзера с самым большим числом. Если их несколько, выводим всех.

вывод:

**username** - философ чата (345 сообщений по медиане)
// если несколько
**username1**, **username2** - философы чата (345 сообщений по медиане)

Доработать ачивку Философ чата

Ограничить выборку сообщений до месяца, но не так чтобы отсчёт был от текущей даты -30, а с первого дня каждого месяца. То есть сообщения написанные 30-31 числа прошлого месяца не учитывались.

Реализовать GET /api/chats

GET /api/chats - Отдает все чаты, в которых участвует юзер

  1. Валидируем данные сессии (https://core.telegram.org/widgets/login#checking-authorization)
  2. В сессии у нас есть данные id, first_name, last_name, username, photo_url, auth_date, hash забираем id. Это id телеграм юзера.
  3. По этому id ищем в бд все чаты, в которых он состоит. Из бд достаем все чаты которые вообще есть в коллекции messages, и фильтруем их методом getChatMember (видимо понадобится либа, которая умеет в запросы в телегу, например telegram-bot-api)
  4. Формируем ответ и если все валидно отдаем:
    200 OK
    {   
      "status": "ok",
      "chats": [
        { "title": "Группа 11б класса", "id": "123123" },
        { "title": "нг 2020", "id": "123124"}
      ] 
    }
  5. Если невалидно, отдаем
    403 Forbidden
    {
      "status": "forbidden"
    }

Добавить реакцию на изменение сообщения

Иногда юзеры пишут с ошибками и получается так, что у нас не актуальная бд.
Поэтому надо апдейтить сообщение по message.message_id, перед этим хорошо будет сделать индекс { message_id: 1 }

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.