Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.git
.github
**/bin
**/obj
test-artifacts
**/TestResults
**/*.lic
.env
**/.env*
6 changes: 6 additions & 0 deletions .github/workflows/_bot-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ on:
regex narrows further to per-bot relevance. Manual triggers always
run the full build regardless.

secrets:
SIXLABORS_LICENSE_KEY:
required: false

jobs:
build:
runs-on: ${{ inputs.runner }}
Expand Down Expand Up @@ -93,6 +97,8 @@ jobs:

- name: Tests
if: steps.should-run.outputs.yes == 'true'
env:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
run: ${{ inputs.test-command }}

- name: Upload dotnet test results
Expand Down
7 changes: 7 additions & 0 deletions .github/workflows/_bot-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ on:
default: true
description: "Open a deploy-failure issue (wakes the SRE agent) when verify-deploy fails"
secrets:
SIXLABORS_LICENSE_KEY:
required: false
WIREGUARD_CONFIG:
required: true
description: >-
Expand Down Expand Up @@ -169,6 +171,8 @@ jobs:
global-json-file: global.json

- name: Tests
env:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
run: ${{ inputs.test-command }}

- name: Upload dotnet test results
Expand Down Expand Up @@ -244,10 +248,13 @@ jobs:

- name: Build and push to GHCR
uses: docker/build-push-action@v5
env:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
with:
push: true
tags: ${{ inputs.docker-image }}:${{ github.sha }}, ${{ inputs.docker-image }}:latest
file: ./src/Dockerfile.bot
secret-envs: ${{ inputs.bot-project == 'CouponHubBot' && 'sixlabors-license=SIXLABORS_LICENSE_KEY' || '' }}
build-args: BOT_PROJECT=${{ inputs.bot-project }}
platforms: linux/arm64

Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/coupon-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ jobs:
dotnet test tests/CouponHubBot.Tests/CouponHubBot.Tests.fsproj -c Release --logger "trx;LogFileName=test-results.trx" --logger "console;verbosity=detailed"
dotnet test tests/CouponHubBot.Ocr.Tests/CouponHubBot.Ocr.Tests.fsproj -c Release --logger "trx;LogFileName=test-results.trx" --logger "console;verbosity=detailed"
bot-paths-regex: '^(src/(BotInfra|CouponHubBot)/|src/Dockerfile\.bot$|tests/(CouponHubBot\.Tests|CouponHubBot\.Ocr\.Tests|BotTestInfra|FakeTgApi|FakeAzureOcrApi)/|Directory\.Build\.props$|Directory\.Packages\.props$|global\.json$)'
secrets:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
1 change: 1 addition & 0 deletions .github/workflows/coupon-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ jobs:
argocd-app-name: coupon-bot
container-name: coupon-bot
secrets:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
# Dedicated single-occupancy peer (Szer/my-infra#111) — do not point
# this back at the shared WIREGUARD_CONFIG secret; concurrent bot
# deploys over one peer flap the WG endpoint (issue #368).
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/coupon-real-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,17 @@ jobs:

- name: Build and push test image
uses: docker/build-push-action@v5
env:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
with:
push: true
# test-<sha> tag — never collides with prod's `:<sha>`/`:latest` tags
# published by _bot-deploy.yml (coupon-deploy.yml:132), same
# convention as alita-real-test.yml.
tags: ${{ env.IMAGE }}:test-${{ github.sha }}
file: ./src/Dockerfile.bot
secret-envs: |
sixlabors-license=SIXLABORS_LICENSE_KEY
build-args: BOT_PROJECT=CouponHubBot
platforms: linux/arm64

Expand Down Expand Up @@ -432,6 +436,7 @@ jobs:
# — this workflow is the one deliberate, workflow_dispatch-only path that is
# meant to set it. Never set this for any other job/workflow.
COUPON_REAL_TESTS: "1"
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
COUPON_REAL_MODE: remote
COUPON_TEST_BOT_USERNAME: ${{ secrets.COUPON_CI_BOT_USERNAME }}
COUPON_TEST_CHAT_ID: ${{ secrets.COUPON_CI_CHAT_ID }}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/multipod-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,5 @@ jobs:
test-command: |
dotnet test tests/MultiPodTests/MultiPodTests.fsproj -c Release --logger "trx;LogFileName=test-results.trx" --logger "console;verbosity=detailed"
bot-paths-regex: '^(src/(BotInfra|VahterBanBot|CouponHubBot)/|src/Dockerfile\.bot$|tests/(MultiPodTests|BotTestInfra|FakeTgApi|FakeAzureOcrApi)/|Directory\.Build\.props$|Directory\.Packages\.props$|global\.json$)'
secrets:
SIXLABORS_LICENSE_KEY: ${{ secrets.SIXLABORS_LICENSE_KEY }}
2 changes: 2 additions & 0 deletions .github/workflows/vahter-upstream-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ on:
- 'tests/Dockerfile.fake'
- 'tests/Directory.Build.targets'
- '.gitignore'
- '.dockerignore'
- 'LICENSE'
- '.github/workflows/_bot-build.yml'
- '.github/upstream/**'
Expand Down Expand Up @@ -94,6 +95,7 @@ jobs:
cp ../monorepo/Directory.Packages.props .
cp ../monorepo/global.json .
cp ../monorepo/.gitignore .
cp ../monorepo/.dockerignore .
cp ../monorepo/LICENSE .
cp ../monorepo/.github/upstream/README.md README.md

Expand Down
4 changes: 4 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@
<PackageReference Include="FSharp.Core" />
</ItemGroup>

<PropertyGroup>
<SixLaborsLicenseKey Condition="'$(SixLaborsLicenseKey)' == ''">$(SIXLABORS_LICENSE_KEY)</SixLaborsLicenseKey>
</PropertyGroup>

</Project>
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,9 @@
<PackageVersion Include="Microsoft.ML" Version="5.0.0" />

<!-- Image processing -->
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.12" />
<PackageVersion Include="SixLabors.ImageSharp" Version="4.1.3" />
<PackageVersion Include="ZXing.Net" Version="0.16.11" />
<PackageVersion Include="ZXing.Net.Bindings.ImageSharp.V3" Version="0.16.18" />
<PackageVersion Include="ZXing.Net.Bindings.ImageSharp.V4" Version="0.16.20" />
<PackageVersion Include="ZXingCpp" Version="0.5.3" />

<!-- Testing -->
Expand Down
2 changes: 1 addition & 1 deletion src/CouponHubBot/CouponHubBot.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" />
<PackageReference Include="ZXing.Net" />
<PackageReference Include="ZXing.Net.Bindings.ImageSharp.V3" />
<PackageReference Include="ZXing.Net.Bindings.ImageSharp.V4" />
<PackageReference Include="ZXingCpp" />
<!-- FakeTimeProvider for the test-only /test/clock/advance endpoint. Stable, ~30KB. -->
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
Expand Down
7 changes: 7 additions & 0 deletions src/CouponHubBot/docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ E2E tests live in `tests/CouponHubBot.Tests/`. They use Testcontainers to spin u

All containers run in a shared Docker network. Tests interact via HTTP against mapped host ports.

### Build license

Set `SIXLABORS_LICENSE_KEY` to the complete contents of the issued Six Labors license for `CouponHubBot`; Release builds validate it.
GitHub Actions uses the repository secret of the same name, and container builds pass it through a build secret mount.

Bot test images use the Docker or Podman CLI. Set `BOT_CONTAINER_ENGINE` to an executable path when neither CLI is on `PATH`.

### Running Tests

```bash
Expand Down
3 changes: 2 additions & 1 deletion src/Dockerfile.bot
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ RUN dotnet restore src/${BOT_PROJECT}/${BOT_PROJECT}.fsproj

COPY src/BotInfra/ src/BotInfra/
COPY src/${BOT_PROJECT}/ src/${BOT_PROJECT}/
RUN dotnet publish src/${BOT_PROJECT}/${BOT_PROJECT}.fsproj -c Release -o /publish
RUN --mount=type=secret,id=sixlabors-license \
SixLaborsLicenseFile=/run/secrets/sixlabors-license dotnet publish src/${BOT_PROJECT}/${BOT_PROJECT}.fsproj -c Release -o /publish

FROM mcr.microsoft.com/dotnet/aspnet:10.0.3-noble AS runtime
ARG BOT_PROJECT
Expand Down
29 changes: 29 additions & 0 deletions src/VahterBanBot/DB.fs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ type UserMessage =
{ chat_id: int64
message_id: int64 }

[<CLIMutable>]
type TriageHistoryMessage =
{ chat_id: int64
text: string | null
created_at: DateTime }

/// Lightweight DTO for TryGetMessageTextAndSender (D2 cache invalidation on the admin-command
/// correction paths, which only have a chat/message reference, not the original TgMessage).
[<CLIMutable>]
Expand Down Expand Up @@ -361,6 +367,29 @@ ON CONFLICT DO NOTHING
(Option.ofObj msg.Text)
(utcNow())

member _.GetTriageHistory(userId: int64, chatId: int64, messageId: int64, ct: System.Threading.CancellationToken) : Task<int64 * TriageHistoryMessage array> = task {
use conn = new NpgsqlConnection(connString)
let sql = """
SELECT COUNT(*) FROM snapshot_message
WHERE user_id = @userId
AND (chat_id, message_id) <> (@chatId, @messageId)
AND created_at < @now;
SELECT chat_id, LEFT(text, 1001) AS text, created_at
FROM snapshot_message
WHERE user_id = @userId
AND (chat_id, message_id) <> (@chatId, @messageId)
AND created_at < @now
ORDER BY created_at DESC, chat_id DESC, message_id DESC
LIMIT 4
"""
use! results = conn.QueryMultipleAsync(CommandDefinition(
sql, {| userId = userId; chatId = chatId; messageId = messageId; now = utcNow() |},
cancellationToken = ct, commandTimeout = 2))
let! count = results.ReadSingleAsync<int64>()
let! rows = results.ReadAsync<TriageHistoryMessage>()
return count, Array.ofSeq rows
}

member _.GetUserMessages(userId: int64) : Task<UserMessage array> =
task {
use conn = new NpgsqlConnection(connString)
Expand Down
Loading
Loading