Skip to content

Commit ed0369a

Browse files
committed
WIP
1 parent 3ff4166 commit ed0369a

29 files changed

+424
-103
lines changed

.deploy/lambda/lib/JProfByBotStack.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {Construct} from 'constructs';
33
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
44
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
55
import * as lambda from 'aws-cdk-lib/aws-lambda';
6+
import {Architecture} from 'aws-cdk-lib/aws-lambda';
67
import * as secrets from 'aws-cdk-lib/aws-secretsmanager';
78
import * as sfn from 'aws-cdk-lib/aws-stepfunctions';
89
import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks';
@@ -12,7 +13,13 @@ export class JProfByBotStack extends cdk.Stack {
1213
constructor(scope: Construct, id: string, props: JProfByBotStackProps) {
1314
super(scope, id, props);
1415

15-
const secretPaymentProviderTokens = new secrets.Secret(this, 'jprof-by-bot-secret-payment-provider-tokens');
16+
const secretPaymentProviderTokens = new secrets.Secret(this, 'jprof-by-bot-secret-payment-provider-tokens', {
17+
secretName: 'jprof-by-bot-secret-payment-provider-tokens',
18+
secretObjectValue: {
19+
test: cdk.SecretValue.unsafePlainText('test'),
20+
production: cdk.SecretValue.unsafePlainText('production'),
21+
}
22+
});
1623

1724
const votesTable = new dynamodb.Table(this, 'jprof-by-bot-table-votes', {
1825
tableName: 'jprof-by-bot-table-votes',
@@ -112,26 +119,22 @@ export class JProfByBotStack extends cdk.Stack {
112119
});
113120

114121
const layerLibGL = new lambda.LayerVersion(this, 'jprof-by-bot-lambda-layer-libGL', {
122+
layerVersionName: 'libGL',
115123
code: lambda.Code.fromAsset('layers/libGL.zip'),
116-
compatibleRuntimes: [lambda.Runtime.JAVA_11],
124+
compatibleArchitectures: [Architecture.ARM_64],
117125
});
118126
const layerLibfontconfig = new lambda.LayerVersion(this, 'jprof-by-bot-lambda-layer-libfontconfig', {
127+
layerVersionName: 'libfontconfig',
119128
code: lambda.Code.fromAsset('layers/libfontconfig.zip'),
120-
compatibleRuntimes: [lambda.Runtime.JAVA_11],
129+
compatibleArchitectures: [Architecture.ARM_64],
121130
});
122-
const layerParametersAndSecretsLambdaExtension = lambda.LayerVersion.fromLayerVersionArn(
123-
this,
124-
'jprof-by-bot-lambda-layer-parametersAndSecretsLambdaExtension',
125-
'arn:aws:lambda:us-east-1:177933569100:layer:AWS-Parameters-and-Secrets-Lambda-Extension:2'
126-
)
127131

128132
const lambdaWebhook = new lambda.Function(this, 'jprof-by-bot-lambda-webhook', {
129133
functionName: 'jprof-by-bot-lambda-webhook',
130134
runtime: lambda.Runtime.JAVA_11,
131135
layers: [
132136
layerLibGL,
133137
layerLibfontconfig,
134-
layerParametersAndSecretsLambdaExtension,
135138
],
136139
timeout: cdk.Duration.seconds(30),
137140
memorySize: 1024,
@@ -153,6 +156,8 @@ export class JProfByBotStack extends cdk.Stack {
153156
},
154157
});
155158

159+
secretPaymentProviderTokens.grantRead(lambdaWebhook);
160+
156161
votesTable.grantReadWriteData(lambdaWebhook);
157162

158163
youtubeChannelsWhitelistTable.grantReadData(lambdaWebhook);

gradle/libs.versions.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ aws-lambda-java-events = { group = "com.amazonaws", name = "aws-lambda-java-even
3737
aws-lambda-java-core = { group = "com.amazonaws", name = "aws-lambda-java-core", version.ref = "aws-lambda-java-core" }
3838
aws-lambda-java-log4j2 = { group = "com.amazonaws", name = "aws-lambda-java-log4j2", version.ref = "aws-lambda-java-log4j2" }
3939
dynamodb = { group = "software.amazon.awssdk", name = "dynamodb", version.ref = "awssdk" }
40+
secretsmanager = { group = "software.amazon.awssdk", name = "secretsmanager", version.ref = "awssdk" }
4041
sfn = { group = "software.amazon.awssdk", name = "sfn", version.ref = "awssdk" }
4142

4243
koin-core = { group = "io.insert-koin", name = "koin-core", version.ref = "koin" }

launchers/lambda/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ plugins {
55

66
dependencies {
77
implementation(libs.bundles.aws.lambda)
8+
implementation(libs.secretsmanager)
89
implementation(libs.koin.core)
910
implementation(libs.bundles.tgbotapi)
1011
implementation(libs.bundles.log4j)
@@ -24,5 +25,6 @@ dependencies {
2425
implementation(project.projects.leetcode)
2526
implementation(project.projects.times.timezones.dynamodb)
2627
implementation(project.projects.times)
28+
implementation(project.projects.shop.provider)
2729
implementation(project.projects.shop)
2830
}

launchers/lambda/src/main/kotlin/by/jprof/telegram/bot/launchers/lambda/JProf.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import by.jprof.telegram.bot.launchers.lambda.config.databaseModule
66
import by.jprof.telegram.bot.launchers.lambda.config.envModule
77
import by.jprof.telegram.bot.launchers.lambda.config.jsonModule
88
import by.jprof.telegram.bot.launchers.lambda.config.pipelineModule
9+
import by.jprof.telegram.bot.launchers.lambda.config.secretsModule
910
import by.jprof.telegram.bot.launchers.lambda.config.sfnModule
1011
import by.jprof.telegram.bot.launchers.lambda.config.telegramModule
1112
import by.jprof.telegram.bot.launchers.lambda.config.youtubeModule
@@ -43,6 +44,7 @@ class JProf : RequestHandler<APIGatewayV2HTTPEvent, APIGatewayV2HTTPResponse>, K
4344
init {
4445
startKoin {
4546
modules(
47+
secretsModule,
4648
envModule,
4749
databaseModule,
4850
jsonModule,

launchers/lambda/src/main/kotlin/by/jprof/telegram/bot/launchers/lambda/config/env.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
package by.jprof.telegram.bot.launchers.lambda.config
22

3+
import by.jprof.telegram.bot.shop.provider.ChatProviderTokens
4+
import kotlinx.serialization.decodeFromString
5+
import kotlinx.serialization.json.Json
36
import org.koin.core.qualifier.named
47
import org.koin.dsl.module
8+
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient
9+
10+
private const val SECRET_PAYMENT_PROVIDER_TOKENS = "jprof-by-bot-secret-payment-provider-tokens"
511

612
const val TOKEN_TELEGRAM_BOT = "TOKEN_TELEGRAM_BOT"
713
const val TOKEN_YOUTUBE_API = "TOKEN_YOUTUBE_API"
@@ -33,4 +39,14 @@ val envModule = module {
3339
System.getenv(variable)!!
3440
}
3541
}
42+
43+
single {
44+
val json: Json = get()
45+
val secrets: SecretsManagerClient = get()
46+
val secret = secrets.getSecretValue {
47+
it.secretId(SECRET_PAYMENT_PROVIDER_TOKENS)
48+
}
49+
50+
json.decodeFromString<ChatProviderTokens>(secret.secretString())
51+
}
3652
}

launchers/lambda/src/main/kotlin/by/jprof/telegram/bot/launchers/lambda/config/pipeline.kt

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ import by.jprof.telegram.bot.quizoji.QuizojiOptionUpdateProcessor
1717
import by.jprof.telegram.bot.quizoji.QuizojiQuestionUpdateProcessor
1818
import by.jprof.telegram.bot.quizoji.QuizojiStartCommandUpdateProcessor
1919
import by.jprof.telegram.bot.quizoji.QuizojiVoteUpdateProcessor
20-
import by.jprof.telegram.bot.shop.PinsShopCommandUpdateProcessor
20+
import by.jprof.telegram.bot.shop.ForwardedPaymentStartCommandUpdateProcessor
21+
import by.jprof.telegram.bot.shop.PinsPreCheckoutQueryUpdateProcessor
2122
import by.jprof.telegram.bot.shop.RichCommandUpdateProcessor
23+
import by.jprof.telegram.bot.shop.RichPreCheckoutQueryUpdateProcessor
2224
import by.jprof.telegram.bot.shop.SupportCommandUpdateProcessor
23-
import by.jprof.telegram.bot.shop.TitleCommandUpdateProcessor
25+
import by.jprof.telegram.bot.shop.SupportPreCheckoutQueryUpdateProcessor
2426
import by.jprof.telegram.bot.times.TimeCommandUpdateProcessor
2527
import by.jprof.telegram.bot.times.TimeZoneCommandUpdateProcessor
2628
import by.jprof.telegram.bot.youtube.YouTubeUpdateProcessor
@@ -113,6 +115,8 @@ val pipelineModule = module {
113115
pinDAO = get(),
114116
unpinScheduler = get(),
115117
bot = get(),
118+
providerTokens = get(),
119+
json = get(),
116120
)
117121
}
118122

@@ -158,27 +162,44 @@ val pipelineModule = module {
158162
)
159163
}
160164

161-
single<UpdateProcessor>(named("PinsShopCommandUpdateProcessor")) {
162-
PinsShopCommandUpdateProcessor(
165+
single<UpdateProcessor>(named("ForwardedPaymentStartCommandUpdateProcessor")) {
166+
ForwardedPaymentStartCommandUpdateProcessor(
163167
bot = get(),
164168
)
165169
}
166170

167-
single<UpdateProcessor>(named("RichCommandUpdateProcessor")) {
168-
RichCommandUpdateProcessor(
171+
single<UpdateProcessor>(named("SupportCommandUpdateProcessor")) {
172+
SupportCommandUpdateProcessor(
173+
bot = get(),
174+
providerTokens = get(),
175+
json = get(),
176+
)
177+
}
178+
single<UpdateProcessor>(named("SupportPreCheckoutQueryUpdateProcessor")) {
179+
SupportPreCheckoutQueryUpdateProcessor(
169180
bot = get(),
181+
json = get(),
170182
)
171183
}
172184

173-
single<UpdateProcessor>(named("TitleCommandUpdateProcessor")) {
174-
TitleCommandUpdateProcessor(
185+
single<UpdateProcessor>(named("PinsPreCheckoutQueryUpdateProcessor")) {
186+
PinsPreCheckoutQueryUpdateProcessor(
175187
bot = get(),
188+
json = get(),
176189
)
177190
}
178191

179-
single<UpdateProcessor>(named("SupportCommandUpdateProcessor")) {
180-
SupportCommandUpdateProcessor(
192+
single<UpdateProcessor>(named("RichCommandUpdateProcessor")) {
193+
RichCommandUpdateProcessor(
194+
bot = get(),
195+
providerTokens = get(),
196+
json = get(),
197+
)
198+
}
199+
single<UpdateProcessor>(named("RichPreCheckoutQueryUpdateProcessor")) {
200+
RichPreCheckoutQueryUpdateProcessor(
181201
bot = get(),
202+
json = get(),
182203
)
183204
}
184205
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package by.jprof.telegram.bot.launchers.lambda.config
2+
3+
import kotlinx.serialization.ExperimentalSerializationApi
4+
import org.koin.dsl.module
5+
import software.amazon.awssdk.services.secretsmanager.SecretsManagerClient
6+
7+
@ExperimentalSerializationApi
8+
val secretsModule = module {
9+
single {
10+
SecretsManagerClient.create()
11+
}
12+
}

pins/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ dependencies {
66
api(project.projects.core)
77
api(libs.tgbotapi.core)
88
api(project.projects.monies)
9+
implementation(project.projects.shop.provider)
10+
implementation(project.projects.shop.payload)
911
implementation(project.projects.pins.dto)
1012
implementation(libs.tgbotapi.extensions.api)
1113
implementation(libs.tgbotapi.extensions.utils)

pins/src/main/kotlin/by/jprof/telegram/bot/pins/PinCommandUpdateProcessor.kt

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import by.jprof.telegram.bot.pins.dao.PinDAO
88
import by.jprof.telegram.bot.pins.dto.Unpin
99
import by.jprof.telegram.bot.pins.model.Pin
1010
import by.jprof.telegram.bot.pins.model.PinDuration
11+
import by.jprof.telegram.bot.pins.model.PinRequest
1112
import by.jprof.telegram.bot.pins.scheduler.UnpinScheduler
1213
import by.jprof.telegram.bot.pins.utils.PinRequestFinder
1314
import by.jprof.telegram.bot.pins.utils.beggar
@@ -16,21 +17,30 @@ import by.jprof.telegram.bot.pins.utils.negativeDuration
1617
import by.jprof.telegram.bot.pins.utils.tooManyPinnedMessages
1718
import by.jprof.telegram.bot.pins.utils.tooPositiveDuration
1819
import by.jprof.telegram.bot.pins.utils.unrecognizedDuration
20+
import by.jprof.telegram.bot.shop.payload.PinsPayload
21+
import by.jprof.telegram.bot.shop.provider.ChatProviderTokens
1922
import dev.inmo.tgbotapi.bot.RequestsExecutor
2023
import dev.inmo.tgbotapi.extensions.api.chat.modify.pinChatMessage
24+
import dev.inmo.tgbotapi.extensions.api.send.payments.sendInvoice
2125
import dev.inmo.tgbotapi.extensions.api.send.reply
2226
import dev.inmo.tgbotapi.types.ParseMode.MarkdownV2
27+
import dev.inmo.tgbotapi.types.payments.LabeledPrice
2328
import dev.inmo.tgbotapi.types.update.abstracts.Update
2429
import dev.inmo.tgbotapi.utils.PreviewFeature
25-
import org.apache.logging.log4j.LogManager
2630
import java.time.Duration
31+
import kotlin.random.Random
32+
import kotlinx.serialization.encodeToString
33+
import kotlinx.serialization.json.Json
34+
import org.apache.logging.log4j.LogManager
2735

2836
@PreviewFeature
2937
class PinCommandUpdateProcessor(
3038
private val moniesDAO: MoniesDAO,
3139
private val pinDAO: PinDAO,
3240
private val unpinScheduler: UnpinScheduler,
3341
private val bot: RequestsExecutor,
42+
private val providerTokens: ChatProviderTokens,
43+
private val json: Json,
3444
private val pinRequestFinder: PinRequestFinder = PinRequestFinder.DEFAULT
3545
) : UpdateProcessor {
3646
companion object {
@@ -47,6 +57,7 @@ class PinCommandUpdateProcessor(
4757

4858
if (pin.message == null) {
4959
bot.reply(to = pin.request, text = help(pins), parseMode = MarkdownV2)
60+
pinsShop(pin)
5061

5162
return
5263
}
@@ -71,6 +82,7 @@ class PinCommandUpdateProcessor(
7182

7283
if (pins < pin.price) {
7384
bot.reply(to = pin.request, text = beggar(pins, pin.price), parseMode = MarkdownV2)
85+
pinsShop(pin)
7486

7587
return
7688
}
@@ -91,9 +103,36 @@ class PinCommandUpdateProcessor(
91103
userId = pin.user.id.chatId
92104
ttl = duration.duration.seconds
93105
})
106+
if (Random.nextInt(4) == 0) {
107+
pinsShop(pin)
108+
}
94109
} catch (e: Exception) {
95110
logger.error("Failed to pin a message", e)
96111
}
97112
}
98113
}
114+
115+
private suspend fun pinsShop(pin: PinRequest) {
116+
val chatProviderToken = providerTokens[pin.request.chat.id.chatId]
117+
118+
if (chatProviderToken != null) {
119+
bot.sendInvoice(
120+
chatId = pin.request.chat.id,
121+
title = "168 пинов",
122+
description = "Неделя закрепа",
123+
payload = json.encodeToString(PinsPayload(
124+
pins = 168,
125+
chat = pin.request.chat.id.chatId,
126+
)),
127+
providerToken = chatProviderToken,
128+
currency = "USD",
129+
prices = listOf(
130+
LabeledPrice("Пины × 168", 200)
131+
),
132+
startParameter = "forwarded_payment",
133+
replyToMessageId = pin.request.messageId,
134+
allowSendingWithoutReply = true,
135+
)
136+
}
137+
}
99138
}

settings.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,5 +35,7 @@ include(":leetcode")
3535
include(":times:timezones")
3636
include(":times:timezones:dynamodb")
3737
include(":times")
38+
include(":shop:provider")
39+
include(":shop:payload")
3840
include(":shop")
3941
include(":launchers:lambda")

shop/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ plugins {
55
dependencies {
66
api(project.projects.core)
77
api(libs.tgbotapi.core)
8+
implementation(project.projects.shop.provider)
9+
implementation(project.projects.shop.payload)
810
implementation(libs.tgbotapi.extensions.api)
911
implementation(libs.tgbotapi.extensions.utils)
1012
implementation(libs.log4j.api)

shop/payload/README.adoc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
= Shop / Payload
2+
3+
Payloads to use in `payload` field of https://core.telegram.org/bots/api#sendinvoice[TG Bot API invoices].

shop/payload/build.gradle.kts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
plugins {
2+
kotlin("jvm")
3+
kotlin("plugin.serialization")
4+
}
5+
6+
dependencies {
7+
implementation(libs.kotlinx.serialization.core)
8+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package by.jprof.telegram.bot.shop.payload
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
sealed class Payload
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package by.jprof.telegram.bot.shop.payload
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
@SerialName("pins")
8+
data class PinsPayload(
9+
val pins: Long,
10+
val chat: Long,
11+
) : Payload()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package by.jprof.telegram.bot.shop.payload
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
@SerialName("rich")
8+
data class RichPayload(
9+
val status: String,
10+
val chat: Long,
11+
) : Payload()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package by.jprof.telegram.bot.shop.payload
2+
3+
import kotlinx.serialization.SerialName
4+
import kotlinx.serialization.Serializable
5+
6+
@Serializable
7+
@SerialName("support")
8+
data class SupportPayload(
9+
val chat: Long,
10+
) : Payload()

0 commit comments

Comments
 (0)