79 lines
2.7 KiB
Perl
Executable File
79 lines
2.7 KiB
Perl
Executable File
#!/usr/bin/env perl
|
|
use strict;
|
|
use warnings;
|
|
use FindBin qw($RealBin);
|
|
use lib "$RealBin/../lib";
|
|
|
|
# Webhook receiver — Mojolicious::Lite app.
|
|
# Binds to 127.0.0.1 only; nginx terminates TLS and proxies inbound updates.
|
|
#
|
|
# Startup:
|
|
# mnemosyne-bot --config /path/to/mnemosyne.conf
|
|
# mnemosyne-bot --config /path/to/mnemosyne.conf daemon (same, explicit)
|
|
#
|
|
# In production (systemd), the unit sets:
|
|
# ExecStart=/usr/bin/perl /opt/mnemosyne/bin/mnemosyne-bot --config /etc/mnemosyne/mnemosyne.conf
|
|
|
|
use Mnemosyne::Config;
|
|
use Mnemosyne::DB;
|
|
use Mnemosyne::Telegram;
|
|
use Mnemosyne::Webhook;
|
|
use Mojolicious::Lite;
|
|
|
|
# ---- parse --config before Mojolicious sees ARGV ----------------------
|
|
my $config_path = "$RealBin/../config/mnemosyne.conf";
|
|
my @passthrough;
|
|
while (@ARGV) {
|
|
my $arg = shift @ARGV;
|
|
if ($arg eq '--config' && @ARGV) {
|
|
$config_path = shift @ARGV;
|
|
} else {
|
|
push @passthrough, $arg;
|
|
}
|
|
}
|
|
@ARGV = @passthrough;
|
|
|
|
# ---- load config & bootstrap ------------------------------------------
|
|
die "Config file not found: $config_path\n" unless -f $config_path;
|
|
my $config = Mnemosyne::Config->new($config_path);
|
|
my $db = Mnemosyne::DB->new($config->get('db', 'path'));
|
|
my $token = $config->get('bot', 'token') or die "bot.token missing in config\n";
|
|
my $secret = $config->get('bot', 'webhook_secret') or die "bot.webhook_secret missing in config\n";
|
|
my $port = $config->get('bot', 'listen_port') // 8443;
|
|
my $wh_url = $config->get('bot', 'webhook_url') or die "bot.webhook_url missing in config\n";
|
|
|
|
my $telegram = Mnemosyne::Telegram->new($token);
|
|
|
|
# Extract the path component from the webhook URL
|
|
# e.g. https://example.com/tghook/abc123 → /tghook/abc123
|
|
(my $wh_path = $wh_url) =~ s{^https?://[^/]+}{};
|
|
$wh_path ||= '/webhook';
|
|
|
|
# ---- webhook endpoint -------------------------------------------------
|
|
post $wh_path => sub {
|
|
my ($c) = @_;
|
|
|
|
# Gate 1: validate Telegram's secret-token header
|
|
my $incoming_secret = $c->req->headers->header('X-Telegram-Bot-Api-Secret-Token') // '';
|
|
unless ($incoming_secret eq $secret) {
|
|
$c->render(text => 'Forbidden', status => 403);
|
|
return;
|
|
}
|
|
|
|
# Respond 200 immediately — Telegram retries on non-2xx or timeouts
|
|
$c->render(text => 'ok', status => 200);
|
|
|
|
# Process the update (synchronous; fast enough for single-user load)
|
|
my $update = $c->req->json;
|
|
eval {
|
|
Mnemosyne::Webhook->handle_update($update, $db, $config, $telegram);
|
|
};
|
|
warn "Webhook processing error: $@\n" if $@;
|
|
};
|
|
|
|
# ---- start ------------------------------------------------------------
|
|
# Default to daemon on localhost; let explicit ARGV override for hypnotoad etc.
|
|
push @ARGV, ('daemon', '-l', "http://127.0.0.1:$port") unless @ARGV;
|
|
|
|
app->start;
|