Skip to content

Commit

Permalink
add premium functions
Browse files Browse the repository at this point in the history
  • Loading branch information
backmeupplz committed Sep 16, 2021
1 parent ce778f8 commit 37924a9
Show file tree
Hide file tree
Showing 16 changed files with 1,008 additions and 50 deletions.
9 changes: 8 additions & 1 deletion .env.sample
Original file line number Diff line number Diff line change
@@ -1,2 +1,9 @@
TOKEN=123
MONGO=mongodb://localhost:27017/shieldy
MONGO=mongodb://localhost:27017/shieldy
ADMIN=123
REPORT_CHAT_ID=123
PREMIUM=false
STRIPE_SECRET_KEY=123
MONTHLY_PRICE=123
YEARLY_PRICE=123
LIFETIME_PRICE=123
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ And you should be good to go! Feel free to fork and submit pull requests. Thanks

- `TOKEN` — Telegram bot token
- `MONGO`— URL of the mongo database
- `ADMIN` — Telegram user ID of the bot administrator
- `REPORT_CHAT_ID` — Telegram chat ID of the channel where the bot should report errors
- `PREMIUM` — Whether the bot should be premium or not
- `STRIPE_SECRET_KEY` — Stripe secret key
- `STRIPE_SIGNING_SECRET` — Stripe signing secret
- `MONTHLY_PRICE` — Monthly Stripe price id of the premium
- `YEARLY_PRICE` — Yearly Stripe price id of the premium
- `LIFETIME_PRICE` — Lifetime Stripe price id of the premium

Also, please, consider looking at `.env.sample`.

Expand Down
Binary file modified design/designs.sketch
Binary file not shown.
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,26 @@
"download-translations": "node scripts/download.js && yarn prettier --single-quote --no-semi --write ./src/helpers/localizations.ts"
},
"dependencies": {
"@koa/cors": "^3.1.0",
"@typegoose/typegoose": "^7.4.7",
"@types/axios": "^0.14.0",
"@types/dotenv": "^8.2.0",
"@types/lodash": "^4.14.165",
"@types/mongoose": "^5.7.36",
"@types/node": "^14.11.10",
"amala": "^7.0.0",
"axios": "^0.21.1",
"concurrently": "^5.3.0",
"dotenv": "^8.2.0",
"glob": "^7.1.7",
"koa": "^2.13.1",
"koa-bodyparser": "^4.3.0",
"koa-router": "^10.1.1",
"lodash": "^4.17.20",
"module-alias": "^2.2.2",
"mongoose": "^5.10.9",
"sharp": "^0.28.3",
"stripe": "^8.175.0",
"svg-captcha": "^1.4.0",
"tall": "^3.1.0",
"telegraf": "git+https://github.com/backmeupplz/telegraf.git#ccef1dc6c811359d4d36667b57237bfba74841b1",
Expand Down
12 changes: 10 additions & 2 deletions src/commands/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,25 @@ export function sendHelp(ctx: Context) {
Date.now() / 1000 - ctx.update.message?.date
)
}
return ctx.replyWithMarkdown(strings(ctx.dbchat, 'helpShieldy'), {
return ctx.replyWithMarkdown(getHelpText(ctx), {
disable_web_page_preview: true,
})
}

export function sendHelpSafe(ctx: Context) {
try {
return ctx.replyWithMarkdown(strings(ctx.dbchat, 'helpShieldy'), {
return ctx.replyWithMarkdown(getHelpText(ctx), {
disable_web_page_preview: true,
})
} catch {
// Do nothing
}
}

function getHelpText(ctx: Context) {
let text = strings(ctx.dbchat, 'helpShieldy')
if (process.env.PREMIUM === 'true') {
text += '\n\n' + strings(ctx.dbchat, 'subscription')
}
return text
}
141 changes: 141 additions & 0 deletions src/commands/subscription.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import { strings } from '@helpers/strings'
import { checkIfGroup } from '@middlewares/checkIfGroup'
import { Telegraf, Context } from 'telegraf'
import { checkLock } from '@middlewares/checkLock'
import { stripe } from '@helpers/stripe'
import { Language } from '@models/Chat'
import Stripe from 'stripe'

const prices = {
monthly: process.env.MONTHLY_PRICE,
yearly: process.env.YEARLY_PRICE,
lifetime: process.env.LIFETIME_PRICE,
}

export function setupSubscription(bot: Telegraf<Context>) {
bot.command('subscription', checkIfGroup, checkLock, async (ctx) => {
if (!ctx.dbchat.subscriptionId) {
return sendSubscriptionButtons(ctx)
}
if (
ctx.from.id !== parseInt(process.env.ADMIN) &&
ctx.from?.username !== 'GroupAnonymousBot' &&
!ctx.isAdministrator
) {
try {
await ctx.deleteMessage()
} catch {
// do nothing
}
return
}
return sendManageSubscription(ctx)
})
}

export async function sendSubscriptionButtons(ctx: Context) {
await ctx.telegram.sendChatAction(ctx.chat.id, 'typing')
return ctx.reply(strings(ctx.dbchat, 'noSubscription'), {
reply_markup: {
inline_keyboard: await subscriptionsKeyboard(ctx),
},
})
}

export async function sendManageSubscription(ctx: Context) {
const keyboard = []
const subscription = await stripe.subscriptions.retrieve(
ctx.dbchat.subscriptionId
)
const customerId = subscription.customer as string
const url = (
await stripe.billingPortal.sessions.create({
customer: customerId,
})
).url
keyboard.push([
{
text: strings(ctx.dbchat, 'manageSubscription'),
url,
},
])
return ctx.reply(strings(ctx.dbchat, 'manageSubscription'), {
reply_markup: {
inline_keyboard: keyboard,
},
})
}

async function subscriptionsKeyboard(ctx: Context) {
let unsafeLocale = ctx.dbchat.language || 'en'
if (unsafeLocale === Language.UKRAINIAN) {
unsafeLocale = 'en'
}
const locale = unsafeLocale as Stripe.Checkout.Session.Locale
const getBackUrl = 'https://t.me/shieldy_premium_bot'
const monthlySession = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price: prices.monthly,
quantity: 1,
},
],
success_url: getBackUrl,
cancel_url: getBackUrl,
client_reference_id: `${ctx.chat.id}`,
locale,
mode: 'subscription',
allow_promotion_codes: true,
})
const yearlySession = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price: prices.yearly,
quantity: 1,
},
],
success_url: getBackUrl,
cancel_url: getBackUrl,
client_reference_id: `${ctx.chat.id}`,
locale,
mode: 'subscription',
allow_promotion_codes: true,
})
const lifetimeSession = await stripe.checkout.sessions.create({
payment_method_types: ['card'],
line_items: [
{
price: prices.lifetime,
quantity: 1,
},
],
success_url: getBackUrl,
cancel_url: getBackUrl,
client_reference_id: `${ctx.chat.id}`,
locale,
mode: 'payment',
allow_promotion_codes: true,
})
return [
[
{
text: `$15/${strings(ctx.dbchat, 'monthly')}`,
url: monthlySession.url,
},
],
[
{
text: `$108/${strings(ctx.dbchat, 'yearly')}`,
url: yearlySession.url,
},
],
[
{
text: `$450 ${strings(ctx.dbchat, 'lifetime')}`,
url: lifetimeSession.url,
},
],
]
}
56 changes: 56 additions & 0 deletions src/controllers/webhook.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { ChatModel, SubscriptionStatus } from '@models/Chat'
import { Context } from 'koa'
import { Controller, Ctx, Post } from 'amala'
import { stripe } from '@helpers/stripe'

@Controller('/webhook')
export default class WebhookController {
@Post('/')
async webhook(@Ctx() ctx: Context) {
try {
// Construct event
const event = stripe.webhooks.constructEvent(
String(ctx.request.rawBody),
ctx.headers['stripe-signature'],
process.env.STRIPE_SIGNING_SECRET
)
// Handle event
if (event.type === 'customer.subscription.deleted') {
const anyData = event.data.object as any
const subscriptionId = anyData.id
const chat = await ChatModel.findOne({ subscriptionId })
if (!chat) {
return ctx.throw(
400,
`Webhook Error: No chat found for subscription id ${subscriptionId}`
)
}
if (chat.subscriptionStatus !== SubscriptionStatus.lifetime) {
chat.subscriptionStatus = SubscriptionStatus.inactive
}
await chat.save()
} else if (event.type === 'checkout.session.completed') {
const anyData = event.data.object as any
const chatId = +anyData.client_reference_id
const chat = await ChatModel.findOne({ id: chatId })
if (!chat) {
return ctx.throw(
400,
`Webhook Error: No chat found with id ${chatId}`
)
}
if (anyData.mode === 'subscription') {
chat.subscriptionId = anyData.subscription
chat.subscriptionStatus = SubscriptionStatus.active
} else {
chat.subscriptionStatus = SubscriptionStatus.lifetime
}
await chat.save()
}
// Respond
return { received: true }
} catch (err) {
return ctx.throw(400, `Webhook Error: ${err.message}`)
}
}
}
37 changes: 34 additions & 3 deletions src/helpers/localizations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1759,7 +1759,38 @@ export const localizations = {
en:
'The 1inch Network unites decentralized protocols whose synergy enables the most lucrative, fastest and protected operations in the DeFi space.',
ru:
'1inch Network объединяет несколько децентрализованных протоколов, синергия которых обеспечивает самые выгодные, быстрые и безопасные транзакции в индустрии DeFi.',

}
'1inch Network объединяет несколько децентрализованных протоколов, синергия которых обеспечивает самые выгодные, быстрые и безопасные транзакции в индустрии DeFi.',
},
subscription: {
en: '/subscription — Manage subscription',
ru: '/subscription — Управление подпиской',
},
noSubscription: {
en:
'This is the premium version of Shieldy. If you want to use a free version — check out @shieldy_bot. In order to use this version of the bot, you will have to purchase a subscription below. The premium version is hosted on dedicated servers that are used by only the premium users.',
ru:
'Это премиум версия Shieldy. Если вы хотите использовать бесплатную версию — попробуйте @shieldy_bot. Для использования этой версии бота вам нужно приобрести подписку ниже. Премиум версия хостится на отдельных серверах, которые используются только премиум пользователями.',
},
subscriptionInfo: {
en:
'Thank you a lot for the support! You can manage your subscription with the button below.',
ru:
'Спасибо вам огромное за поддержку! Вы можете управлять вашей подпиской с помощью кнопки ниже.',
},
monthly: {
en: 'month',
ru: 'месяц',
},
yearly: {
en: 'year',
ru: 'год',
},
lifetime: {
en: 'once and forever',
ru: 'один раз навсегда',
},
manageSubscription: {
en: 'Manage subscription',
ru: 'Управление подпиской',
},
}
Loading

0 comments on commit 37924a9

Please sign in to comment.