Skip to content

Commit

Permalink
Custom_buy (#10)
Browse files Browse the repository at this point in the history
* Add custom buy option

* Add admin message

* Add error handling
  • Loading branch information
christiansegercrantz authored May 13, 2024
1 parent d839b79 commit f514eb8
Show file tree
Hide file tree
Showing 6 changed files with 154 additions and 24 deletions.
21 changes: 11 additions & 10 deletions backend/src/admin/product.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
getProductById,
getProducts,
} from '../products.js'
import { formatButtonArray } from '../utils.js'
import { ContextWithScenes } from './scene.js'
import { confirmOrAbortButton, formatButtonArray } from '../utils.js'
import { ContextWithScenes } from '../scene.js'

const bot = new Composer<ContextWithScenes>()

Expand Down Expand Up @@ -142,6 +142,7 @@ const addProductScene = new Scenes.WizardScene<ContextWithScenes>(
},
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
ctx.editMessageReplyMarkup(undefined)
const decision = ctx.callbackQuery.data
if (decision === 'confirm') {
const product = {
Expand Down Expand Up @@ -214,6 +215,7 @@ const editProductScene = new Scenes.WizardScene<ContextWithScenes>(
},
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
ctx.editMessageReplyMarkup(undefined)
const productId = Number(ctx.callbackQuery.data)
ctx.scene.session.product = (await getProductById(productId)).rows[0]
ctx.reply(
Expand All @@ -229,6 +231,7 @@ const editProductScene = new Scenes.WizardScene<ContextWithScenes>(
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
if (ctx.callbackQuery.data === 'skip') {
ctx.editMessageReplyMarkup(undefined)
ctx.reply(
`Produktens nya beskrivning? Nu har den beskrviningen "${ctx.scene.session.product.description}"`,
{
Expand All @@ -254,6 +257,7 @@ const editProductScene = new Scenes.WizardScene<ContextWithScenes>(
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
if (ctx.callbackQuery.data === 'skip') {
ctx.editMessageReplyMarkup(undefined)
ctx.reply(
`Produktens nya pris (i positiva cent)? Nu är det ${-ctx.scene.session
.product.price_cents}`,
Expand Down Expand Up @@ -281,6 +285,7 @@ const editProductScene = new Scenes.WizardScene<ContextWithScenes>(
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
if (ctx.callbackQuery.data === 'skip') {
ctx.editMessageReplyMarkup(undefined)
const updatedProduct = ctx.scene.session.product
await confirmOrAbortButtonForEdit(ctx, updatedProduct)

Expand All @@ -305,6 +310,7 @@ const editProductScene = new Scenes.WizardScene<ContextWithScenes>(
},
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
ctx.editMessageReplyMarkup(undefined)
const decision = ctx.callbackQuery.data
if (decision === 'confirm') {
try {
Expand Down Expand Up @@ -338,9 +344,9 @@ async function confirmOrAbortButtonForEdit(
const originalProduct = (await getProductById(product.id)).rows[0]
ctx.reply(
`Följande product kommer att uppdateras:\n` +
`\t${originalProduct.name} --> ${product.name}\n` +
`\t${originalProduct.description} --> ${product.description}\n` +
`\t${-originalProduct.price_cents} --> ${-product.price_cents}\n`,
`\t\t\tKommando: ${originalProduct.name} --> ${product.name}\n` +
`\t\t\tFörklaring: ${originalProduct.description} --> ${product.description}\n` +
`\t\t\tPris: ${-originalProduct.price_cents} --> ${-product.price_cents}\n`,
{
...confirmOrAbortButton,
}
Expand All @@ -351,11 +357,6 @@ async function confirmOrAbortButtonForEdit(

//#region Misc & Export

const confirmOrAbortButton = Markup.inlineKeyboard([
Markup.button.callback('Godkänn', 'confirm'),
Markup.button.callback('Avbryt', 'abort'),
])

const stage = new Scenes.Stage([addProductScene, editProductScene])
bot.use(stage.middleware())

Expand Down
2 changes: 1 addition & 1 deletion backend/src/admin/saldo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '../transactions.js'
import { createCsv, formatTransaction } from '../utils.js'
import { config } from '../config.js'
import { ContextWithScenes } from './scene.js'
import { ContextWithScenes } from '../scene.js'

//#region Misc

Expand Down
4 changes: 2 additions & 2 deletions backend/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ export const config = {
botToken: typedProcessEnv.BOT_TOKEN,
chatId: typedProcessEnv.CHAT_ID,
adminChatId: typedProcessEnv.ADMIN_CHAT_ID,
bankAccount : {
bankAccount: {
number: typedProcessEnv.BANK_ACCOUNT_NUMMER,
name: typedProcessEnv.BANK_ACCOUNT_NAME,
ref: typedProcessEnv.BANK_ACCOUNT_REF,
}
},
}
135 changes: 126 additions & 9 deletions backend/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//#region Imports & Init
import { Context, Markup, Telegraf, session } from 'telegraf'
import { Context, Markup, Telegraf, session, Scenes } from 'telegraf'
import { config } from './config.js'
import {
TransactionInsert,
Expand All @@ -10,26 +10,27 @@ import {
import { Message, Update } from '@telegraf/types'
import adminCommands from './admin/index.js'
import { productsToArray } from './admin/product.js'
import { ContextWithScenes } from './admin/scene.js'
import { ContextWithScenes } from './scene.js'
import productCommands from './admin/product.js'
import {
centsToEuroString,
centsToEuroString as formatCentsToEuroString,
confirmOrAbortButton,
formatButtonArray,
formatDateToString,
formatName,
} from './utils.js'

/*
Toiveiden tynnyri:
- En 'vapaa myynti' command med description och summa
- Tyhjä
*/

const bot = new Telegraf<ContextWithScenes>(config.botToken)

const info_message = `Hej, välkommen till STF spik bot!
Här kan du köra köp och kolla ditt saldo.
Du hittar alla commandon under "Menu" med beskrivning.
Märk att du inte kan ångra ett köp, så var aktsam!
Märk att du kan endast ångra de senaste köpet, så var aktsam!
Ifall något underligt sker ska du vara i kontakt med Croupiären!\n
Oss väl och ingen illa!`

Expand Down Expand Up @@ -180,7 +181,7 @@ bot.command('historia', async (ctx) => {
parsedHistory.forEach((row) => {
res +=
`\n${formatDateToString(row.created_at, true)}, ` +
`${centsToEuroString(-row.amount_cents)}, ` +
`${formatCentsToEuroString(-row.amount_cents)}, ` +
`${row.description}`
})
res += '```'
Expand Down Expand Up @@ -220,7 +221,7 @@ bot.command('undo', async (ctx) => {
'Följande transaktion har ångrats: \n' +
`\t\tTid: ${formatDateToString(latestTransaction.created_at, true)}\n` +
`\t\tProdukt: ${latestTransaction.description}\n` +
`\t\tPris: ${centsToEuroString(latestTransaction.amount_cents)}`
`\t\tPris: ${formatCentsToEuroString(latestTransaction.amount_cents)}`

ctx.reply(message)
console.log(
Expand All @@ -236,6 +237,118 @@ bot.command('undo', async (ctx) => {

//endregion

//#region Custom buy
const buyOtherScene = new Scenes.WizardScene<ContextWithScenes>(
'buy_other_scene',
async (ctx) => {
ctx.reply('Vad är de du vill köpa?')
ctx.scene.session.customBuy = {
description: '',
priceCents: '',
}
return ctx.wizard.next()
},
async (ctx) => {
if (ctx.message && 'text' in ctx.message) {
ctx.scene.session.customBuy.description = ctx.message.text
ctx.reply('Produktens pris (i positiva cent)?')
return ctx.wizard.next()
} else {
ctx.reply('Du måst skriva en text')
}
},
async (ctx) => {
if (ctx.message && 'text' in ctx.message) {
try {
if (isNaN(Number(ctx.message.text)) || Number(ctx.message.text) < 0) {
return ctx.reply(
'Priset måste vara ett positivt tal, det läggs sedan in i databasen som negativt!'
)
}
ctx.scene.session.customBuy.priceCents = `-` + ctx.message.text

const customBuy = ctx.scene.session.customBuy
confirmOrAbortReplyForCustomBuy(ctx, customBuy)
return ctx.wizard.next()
} catch (e) {
console.log('Error found: ', e)
ctx.reply(
'Ett oväntat fel har inträffat. Pröva igen och/eller kontakta croupiären!'
)
return ctx.scene.leave()
}
}
},
async (ctx) => {
if (ctx.callbackQuery && 'data' in ctx.callbackQuery) {
ctx.editMessageReplyMarkup(undefined)
const decision = ctx.callbackQuery.data
if (decision === 'confirm') {
try {
const customBuy = ctx.scene.session.customBuy
await purchaseItemForMember({
userId: ctx.from!.id,
userName: formatName({ ...ctx.from! }),
description: customBuy.description,
amountCents: customBuy.priceCents,
})

const adminMessage =
`Användare ${formatName(ctx.from!)} köpte custom produkt:\n` +
`\t\t\tVad: ${customBuy.description}\n` +
`\t\t\tPris: ${formatCentsToEuroString(-customBuy.priceCents)}\n`

ctx.telegram.sendMessage(config.adminChatId, adminMessage)
} catch (e) {
console.log('Failed to purchase item:', e)
ctx.reply('Köpet misslyckades, klaga till croupieren')
}
try {
const balance = await getBalanceForMember(ctx.from!.id)
ctx.reply(`Köpet lyckades! Ditt saldo är nu ${balance}€`)
} catch (e) {
console.log('Failed to get balance:', e)
ctx.reply(
'Köpet lyckades, men kunde inte hämta saldo. Klaga till croupieren'
)
}
return ctx.scene.leave()
} else if (decision === 'abort') {
ctx.reply('Köpet avbrutet.')
return ctx.scene.leave()
}
} else {
ctx.reply('Du måst trycka på endera alternativ')
}
}
)

function confirmOrAbortReplyForCustomBuy(
ctx: ContextWithScenes,
customBuy: {
description: string
priceCents: string
}
) {
ctx.reply(
`Följande inköp kommer att läggas till åt Er:\n` +
`\t\t\tVad: ${customBuy.description}\n` +
`\t\t\tPris: ${formatCentsToEuroString(-customBuy.priceCents)}\n`,
{
...confirmOrAbortButton,
}
)
}

const stage = new Scenes.Stage([buyOtherScene])
bot.use(stage.middleware())

bot.command('kop_ovrigt', async (ctx) => {
await ctx.scene.enter('buy_other_scene')
})

//endregion

//#region Misc commands

bot.command('saldo', async (ctx) => {
Expand All @@ -258,11 +371,15 @@ bot.telegram.setMyCommands([
Number(price_cents) / -100
).toFixed(2)}€`,
})),
{ command: 'saldo', description: 'Kontrollera saldo' },
{ command: 'info', description: 'Visar information om bottens användning' },
{ command: 'meny', description: 'Tar upp köp menyn för alla produkter' },
{
command: 'kop_ovrigt',
description: 'Köp något som inte finns bland nuvarande producter',
},
{ command: 'saldo', description: 'Kontrollera saldo' },
{ command: 'historia', description: 'Se din egna transaktionshistorik' },
{ command: 'undo', description: 'Ångra ditt senaste köp' },
{ command: 'info', description: 'Visar information om bottens användning' },
])

// Admin middleware is used for all commands added after this line!
Expand Down
10 changes: 8 additions & 2 deletions backend/src/admin/scene.ts → backend/src/scene.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { Scenes } from 'telegraf'
import { Product, ProductIn } from '../products.js'
import { TransactionInsert } from '../transactions.js'
import { User } from '@telegraf/types'
import { Product, ProductIn } from './products.js'
import { TransactionInsert } from './transactions.js'

interface MyWizardSession extends Scenes.WizardSessionData {
// available in scene context under ctx.scene.session
newProduct: ProductIn
product: Product
transactions: TransactionInsert[]
customBuy: {
description: string
priceCents: string
}
from: User
}

export type ContextWithScenes = Scenes.WizardContext<MyWizardSession>
6 changes: 6 additions & 0 deletions backend/src/utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Markup } from 'telegraf'
import { QueryResult } from 'pg'

export const createCsv = (queryResult: QueryResult<any>) => {
Expand Down Expand Up @@ -84,3 +85,8 @@ export const formatTransaction = (
`${description}`
)
}

export const confirmOrAbortButton = Markup.inlineKeyboard([
Markup.button.callback('Godkänn', 'confirm'),
Markup.button.callback('Avbryt', 'abort'),
])

0 comments on commit f514eb8

Please # to comment.