manuelalferez / flowinance

An ultra-fast financial management app with automatic transaction categorization.
https://flowinance.com
GNU General Public License v3.0
25 stars 33 forks source link

Add support for telegram integrations Create a component for integration #122

Open vizzv opened 1 month ago

vizzv commented 1 month ago

Pull Request Template

What

Add Support for Integration of Telegram #82 , Add new Component For Integration

How

Added Support for telegram bot integration dynamically, We just need to add our bot key to one environment variable called NEXT_PUBLIC_TELEGRAM_BOT_API and need to register setWebhook by doing post request to https://api.telegram.org/bot[NEXT_PUBLIC_TELEGRAM_BOT_API]/setWebhook with hook url in body like { "url":"https://{appurl}/api/telegram" }

Additional Notes

How user registers ?

I am attaching video :(How user use the funtionality)

https://github.com/user-attachments/assets/1ae307c7-f9a5-46b7-967c-d0e6ede039c8

@manuelalferez We can add new table in db for storing cron job in future .we just need userId,chatId and cronJobId to store in db . Please review and give me suggestions and changes if any.

vercel[bot] commented 1 month ago

@vizzv is attempting to deploy a commit to the manuelalferez's projects Team on Vercel.

A member of the Team first needs to authorize it.

vercel[bot] commented 1 month ago

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
flowinance ❌ Failed (Inspect) Oct 17, 2024 0:56am
manuelalferez commented 1 month ago

Vercel deployment has failed because:

[14:56:05.652] Running build in Washington, D.C., USA (East) – iad1
[14:56:05.815] Cloning github.com/manuelalferez/flowinance (Branch: feature/telegramIntegration, Commit: 634bb89)
[14:56:06.205] Cloning completed: 386.339ms
[14:56:10.341] Restored build cache from previous deployment (4gmkxTKUCQLZfzqXh2ynRWcsMfNt)
[14:56:10.434] Running "vercel build"
[14:56:11.578] Vercel CLI 37.8.0
[14:56:12.072] Installing dependencies...
[14:56:13.799] 
[14:56:13.800] up to date in 1s
[14:56:13.800] 
[14:56:13.801] 123 packages are looking for funding
[14:56:13.801]   run `npm fund` for details
[14:56:13.809] Detected Next.js version: 13.4.19
[14:56:13.815] Running "npm run build"
[14:56:13.960] 
[14:56:13.961] > flowinance@0.1.0 build
[14:56:13.961] > next build
[14:56:13.961] 
[14:56:14.522] - warn You have enabled experimental feature (serverActions) in next.config.js.
[14:56:14.523] - warn Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.
[14:56:14.523] 
[14:56:14.614] - info Creating an optimized production build...
[14:56:32.160] Failed to compile.
[14:56:32.160] 
[14:56:32.161] ./src/app/api/telegram/route.ts
[14:56:32.161] Module not found: Can't resolve 'dotenv'
[14:56:32.161] 
[14:56:32.161] https://nextjs.org/docs/messages/module-not-found
[14:56:32.161] 
[14:56:32.161] ./src/lib/cronjobs.ts
[14:56:32.161] Module not found: Can't resolve 'node-cron'
[14:56:32.161] 
[14:56:32.162] https://nextjs.org/docs/messages/module-not-found
[14:56:32.162] 
[14:56:32.162] Import trace for requested module:
[14:56:32.162] ./src/app/api/telegram/route.ts
[14:56:32.164] 
[14:56:32.166] 
[14:56:32.170] > Build failed because of webpack errors
[14:56:32.236] Error: Command "npm run build" exited with 1
[14:56:32.801] 
manuelalferez commented 1 month ago

@vizzv let me see if I understand correctly, we have to create a new table with the following fields:

model cron_jobs {
  id         BigInt   @id @default(autoincrement())
  user_id    String
  chat_id    String
  cron_job_id String
  created_at DateTime @default(now()) @db.Timestamptz
}

Could you confirm that?

Also, please let me know if I need to take anything else into account to start running the project with this new feature.

I have only added some small comments to the code. Overall everything looks very clean and well structured. I would like to interact with the new functionality and with that I will add some last comments/questions (if I have some), as it is a big feature.

Amazing job man 🎉

vizzv commented 1 month ago

@manuelalferez ,Currently when Cron job is created It has id as same as chatId, So we need to change this and add something more unique for cron job id like uuid or other unique thing ,So in future we can use this same cron job functionality in any other features or functionality . for other fields you got it correct.Please let me know your thoughts on cron job id.

vizzv commented 1 month ago

For running project without any build fails and error, please add node cron and dotenv packages using

npm i node-cron dotenv

And create a telegram bot using bot father in telegram and set its environment variable NEXT_PUBLIC_TELEGRAM_BOT_API = new bot's key.

manuelalferez commented 1 month ago

@vizzv I am still having difficulties configuring to run flowinance with the bot. We are going to do two things:

This way we will have the project as you have it configured, and we can test it. Thanks!

vizzv commented 1 month ago

@manuelalferez I changed .env.example and schema.prisma both files. Please make sure you set webhook to right url by just making POST request to https://api.telegram.org/bot[BOT_KEY]/setWebhook and with body as { "url":"[SITE_URL]/api/telegram" } ,BOT_KEY is out telegram bot key provided when we created. SITE_URL is hosted site url in our case https://flowinance.com

vizzv commented 4 weeks ago

When we call api/telegram we need to get user from email as we dont have any session or any thing so we need to create supabase client ,But we does not have any user table so we need to get them using supabase.auth.admin.listUsers() and for that we need supabase service key. As user is not logged in and making request from telegram So we need this. May above explanation is enough.

manuelalferez commented 4 weeks ago

@vizzv let me get this straight, if the user without a Flowinance account tries to log in from the bot, what happens?

manuelalferez commented 4 weeks ago

Failed to compile:

./src/lib/utils.ts
Error: 
  x the name `currencies` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:7:1]
  7 | import { format, parseISO } from "date-fns";
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
    :          ^^^^^|^^^^
    :               `-- previous definition of `currencies` here
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
 12 | import { currencies,INTEGRATIONS } from "./constants";
    :          ^^^^^|^^^^
    :               `-- `currencies` redefined here
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
    `----

  x the name `INTEGRATIONS` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:7:1]
  7 | import { format, parseISO } from "date-fns";
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
    :                     ^^^^^^|^^^^^
    :                           `-- previous definition of `INTEGRATIONS` here
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
 12 | import { currencies,INTEGRATIONS } from "./constants";
    :                     ^^^^^^|^^^^^
    :                           `-- `INTEGRATIONS` redefined here
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
    `----

  x the name `getInvested` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:8:1]
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :          ^^^^^|^^^^^
    :               `-- previous definition of `getInvested` here
 12 | import { currencies,INTEGRATIONS } from "./constants";
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :          ^^^^^|^^^^^
    :               `-- `getInvested` redefined here
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
 16 | const DEFAULT_CURRENCY = currencies.at(0)!.name;
    `----

  x the name `getSavings` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:8:1]
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                       ^^^^^|^^^^
    :                            `-- previous definition of `getSavings` here
 12 | import { currencies,INTEGRATIONS } from "./constants";
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                       ^^^^^|^^^^
    :                            `-- `getSavings` redefined here
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
 16 | const DEFAULT_CURRENCY = currencies.at(0)!.name;
    `----

  x the name `getSpecialCategories` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:8:1]
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                   ^^^^^^^^^^|^^^^^^^^^
    :                                             `-- previous definition of `getSpecialCategories` here
 12 | import { currencies,INTEGRATIONS } from "./constants";
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                   ^^^^^^^^^^|^^^^^^^^^
    :                                             `-- `getSpecialCategories` redefined here
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
 16 | const DEFAULT_CURRENCY = currencies.at(0)!.name;
    `----

  x the name `getTotalExpenses` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:8:1]
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                                         ^^^^^^^^|^^^^^^^
    :                                                                 `-- previous definition of `getTotalExpenses` here
 12 | import { currencies,INTEGRATIONS } from "./constants";
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                                         ^^^^^^^^|^^^^^^^
    :                                                                 `-- `getTotalExpenses` redefined here
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
 16 | const DEFAULT_CURRENCY = currencies.at(0)!.name;
    `----

  x the name `getTotalIncomes` is defined multiple times
    ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:8:1]
  8 | import moment from "moment";
  9 | import { EXPENSES_CATEGORIES } from "./categories";
 10 | import { currencies,INTEGRATIONS } from "./constants";
 11 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                                                           ^^^^^^^|^^^^^^^
    :                                                                                  `-- previous definition of `getTotalIncomes` here
 12 | import { currencies,INTEGRATIONS } from "./constants";
 13 | import { getInvested, getSavings, getSpecialCategories, getTotalExpenses, getTotalIncomes } from "./calculations";
    :                                                                           ^^^^^^^|^^^^^^^
    :                                                                                  `-- `getTotalIncomes` redefined here
 14 | 
 15 | const DEFAULT_DELIMITER = ";";
 16 | const DEFAULT_CURRENCY = currencies.at(0)!.name;
    `----

  x the name `getCurrencyFromUserId` is defined multiple times
     ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:667:1]
 667 |   return currency;
 668 | }
 669 | 
 670 | async function getCurrencyFromUserId(
     :                ^^^^^^^^^^|^^^^^^^^^^
     :                          `-- previous definition of `getCurrencyFromUserId` here
 671 |   supabase: SupabaseClient<any, "public", any>,
 672 |   userId:string
 673 | ) 
 674 | {
 675 |   const { data, error } = await supabase
 676 |     .from(SETTINGS_TABLE)
 677 |     .select("currency")
 678 |     .eq("user_id", userId);
 679 |   if (error) {
 680 |     console.log(`Error getting currency for the user ${userId}: `, error);
 681 |     return;
 682 |   }
 683 |   if (!data || data.length === 0) {
 684 |     return;
 685 |   }
 686 |   return data[0].currency;
 687 | }
 688 | 
 689 | async function getCurrencyFromUserId(
     :                ^^^^^^^^^^|^^^^^^^^^^
     :                          `-- `getCurrencyFromUserId` redefined here
 690 |   supabase: SupabaseClient<any, "public", any>,
 691 |   userId:string
 692 | ) 
     `----

  x the name `getWeeklyStatOfUser` is defined multiple times
     ,-[/Users/supersu/github/manuelalferez/flowinance/src/lib/utils.ts:744:1]
 744 |   localStorage.setItem(LocalStorage.settingsUpdated, "true");
 745 | }
 746 | 
 747 | export async function getWeeklyStatOfUser(
     :                       ^^^^^^^^^|^^^^^^^^^
     :                                `-- previous definition of `getWeeklyStatOfUser` here
 748 |   supabase: SupabaseClient<any, "public", any>,
 749 |   email:string
 750 | )
 751 | {
 752 |   var userId:string="";
 753 |   const users = (await supabase.auth.admin.listUsers()).data.users;
 754 |   users.forEach((x)=>{if(x.email===email){userId=x.id;}});
 755 |   if (!userId) {
 756 |     return null;
 757 |   }
 758 | 
 759 |   const { data,error } = await supabase
 760 |     .from(TRANSACTIONS_TABLE)
 761 |     .select("*")
 762 |     .eq("user_id", userId);
 763 |   if (error) {
 764 |     console.log(`Error setting currency for the user ${userId}: `, error);
 765 |     return;
 766 |   }
 767 |   var decryptedData:Transaction[] = []
 768 |   decryptedData = decryptTransactions(data as TransactionSupabase[],userId);
 769 |   const currency = await getCurrencyFromUserId(supabase,userId);
 770 |   const currencySymbol = currencies.find(x=>x.name===currency)?.symbol;
 771 |   const expenses = getTotalExpenses(decryptedData);
 772 |   const incomes = getTotalIncomes(decryptedData);
 773 |   const savings = getSavings(decryptedData);
 774 |   const special = getInvested(decryptedData) + savings;
 775 |   const uncategorized = getSpecialCategories(decryptedData) - special;
 776 |   const balance = incomes - expenses - special + uncategorized;
 777 | 
 778 |   var weekTransactions :{amount:string,concept:string,category:string}[] = []
 779 |   decryptedData.forEach((transaction)=>{
 780 |     weekTransactions.push({amount:`${currencySymbol}${transaction.amount}`,concept:transaction.concept,category:transaction.category})
 781 |   })
 782 |   return {transactions:weekTransactions,balance:`${currencySymbol}`+balance.toFixed(2),expenses:`${currencySymbol}`+expenses.toFixed(2),incomes:`${currencySymbol}`+incomes.toFixed(2),savings:`${currencySymbol}`+savings.toFixed(2)}
 783 | }
 784 | 
 785 | export async function getWeeklyStatOfUser(
     :                       ^^^^^^^^^|^^^^^^^^^
     :                                `-- `getWeeklyStatOfUser` redefined here
 786 |   supabase: SupabaseClient<any, "public", any>,
 787 |   email:string
 788 | )
     `----
vizzv commented 3 weeks ago

@manuelalferez if the user without a Flowinance account tries to log in from the bot, It doesn't able to get anything.

vizzv commented 2 days ago

Hi , @manuelalferez Is this resolved or have any failure in build?