Skip to content

Commit d12ef5f

Browse files
authored
Merge pull request #47 from Quantus-Network/feat/setup-tweet-cap-alert
Setup tweet cap alert
2 parents 8086df5 + cfe5dd6 commit d12ef5f

21 files changed

Lines changed: 579 additions & 34 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ target/
22
.DS_Store
33
.env
44
config/prod.toml
5+
.cursor

config/default.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ client_secret = "lfXc45dZLqYTzP62Ms32EhXinGQzxcIP9TvjJml2B-h0T1nIJK"
5858
api_key = "some-key"
5959
interval_in_hours = 24
6060
keywords = "quantum"
61+
monthly_limit = 15000
62+
alert_threshold = 13500
63+
reset_day = 22
6164

6265
[tg_bot]
6366
base_url = "https://api.telegram.org"
@@ -69,3 +72,6 @@ token = "token"
6972
[raid_leaderboard]
7073
sync_interval_in_hours = 24
7174
tweets_req_interval_in_secs = 60
75+
76+
[alert]
77+
webhook_url = "https://www.webhook_url.com"

config/example.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ client_secret = "example-secret"
6868
api_key = "some-key"
6969
interval_in_hours = 24
7070
keywords = "example"
71+
monthly_limit = 15000
72+
alert_threshold = 13500
73+
reset_day = 22
7174

7275
[tg_bot]
7376
base_url = "https://api.telegram.org"
@@ -80,6 +83,9 @@ token = "token"
8083
sync_interval_in_hours = 24
8184
tweets_req_interval_in_secs = 60
8285

86+
[alert]
87+
webhook_url = "https://www.webhook_url.com"
88+
8389
# Example environment variable overrides:
8490
# TASKMASTER_BLOCKCHAIN__NODE_URL="ws://remote-node:9944"
8591
# TASKMASTER_BLOCKCHAIN__WALLET_PASSWORD="super_secure_password"

config/test.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ client_secret = "test-secret"
5858
api_key = "some-key"
5959
interval_in_hours = 24
6060
keywords = "test"
61+
monthly_limit = 15000
62+
alert_threshold = 13500
63+
reset_day = 22
6164

6265
[tg_bot]
6366
base_url = "https://api.telegram.org"
@@ -69,3 +72,6 @@ token = "token"
6972
[raid_leaderboard]
7073
sync_interval_in_hours = 24
7174
tweets_req_interval_in_secs = 1
75+
76+
[alert]
77+
webhook_url = "https://www.webhook_url.com"
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- Table to track Twitter API usage for the monthly cap
2+
CREATE TABLE IF NOT EXISTS tweet_pull_usage (
3+
period VARCHAR(7) PRIMARY KEY, -- Format: YYYY-MM
4+
tweet_count INTEGER DEFAULT 0,
5+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
6+
);
7+
8+
-- Trigger for updated_at
9+
DROP TRIGGER IF EXISTS set_timestamp ON tweet_pull_usage;
10+
CREATE TRIGGER set_timestamp
11+
BEFORE UPDATE ON tweet_pull_usage
12+
FOR EACH ROW
13+
EXECUTE PROCEDURE trigger_set_timestamp();
14+

scripts/seed_test_tweet_authors.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/bin/bash
2+
set -e
3+
4+
# CONFIG ------------------------------------------
5+
CONTAINER_NAME="task_master_test_db" # Change if your container is named differently
6+
DB_USER="postgres"
7+
DB_NAME="task_master"
8+
SQL_FILE="seed_authors.sql"
9+
# --------------------------------------------------
10+
11+
echo "🔧 Generating seed SQL..."
12+
13+
cat << 'EOF' > $SQL_FILE
14+
INSERT INTO tweet_authors (
15+
id, name, username, followers_count, following_count,
16+
tweet_count, listed_count, like_count, media_count, fetched_at
17+
)
18+
VALUES
19+
('1862779229277954048', 'Yuvi Lightman', 'YuviLightman', 0, 0, 0, 0, 0, 0, NOW())
20+
ON CONFLICT (id) DO UPDATE SET
21+
name = EXCLUDED.name,
22+
username = EXCLUDED.username,
23+
followers_count = EXCLUDED.followers_count,
24+
following_count = EXCLUDED.following_count,
25+
tweet_count = EXCLUDED.tweet_count,
26+
listed_count = EXCLUDED.listed_count,
27+
like_count = EXCLUDED.like_count,
28+
media_count = EXCLUDED.media_count,
29+
fetched_at = NOW();
30+
EOF
31+
32+
echo "📦 Copying SQL file into container ($CONTAINER_NAME)..."
33+
podman cp "$SQL_FILE" "$CONTAINER_NAME":/"$SQL_FILE"
34+
35+
echo "🚀 Running seed script inside Postgres..."
36+
podman exec -it "$CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME" -f "/$SQL_FILE"
37+
38+
echo "🔍 Verifying result..."
39+
podman exec -it "$CONTAINER_NAME" psql -U "$DB_USER" -d "$DB_NAME" -c "SELECT * FROM tweet_authors WHERE id = '1862779229277954048';"
40+
41+
rm -rf "$SQL_FILE"
42+
43+
echo "✅ Seeding complete!"

src/config.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct Config {
1616
pub tweet_sync: TweetSyncConfig,
1717
pub tg_bot: TelegramBotConfig,
1818
pub raid_leaderboard: RaidLeaderboardConfig,
19+
pub alert: AlertConfig,
1920
}
2021

2122
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -74,6 +75,9 @@ pub struct TweetSyncConfig {
7475
pub interval_in_hours: u64,
7576
pub keywords: String,
7677
pub api_key: String,
78+
pub monthly_limit: u32,
79+
pub alert_threshold: u32,
80+
pub reset_day: u32,
7781
}
7882

7983
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -90,6 +94,11 @@ pub struct RaidLeaderboardConfig {
9094
pub tweets_req_interval_in_secs: u64,
9195
}
9296

97+
#[derive(Debug, Clone, Serialize, Deserialize)]
98+
pub struct AlertConfig {
99+
pub webhook_url: String,
100+
}
101+
93102
impl Config {
94103
pub fn load(config_path: &str) -> Result<Self, config::ConfigError> {
95104
let settings = config::Config::builder()
@@ -209,6 +218,9 @@ impl Default for Config {
209218
interval_in_hours: 24,
210219
keywords: "hello".to_string(),
211220
api_key: "key".to_string(),
221+
monthly_limit: 15000,
222+
alert_threshold: 13000,
223+
reset_day: 1,
212224
},
213225
tg_bot: TelegramBotConfig {
214226
base_url: "https://api.telegram.org".to_string(),
@@ -220,6 +232,9 @@ impl Default for Config {
220232
sync_interval_in_hours: 24,
221233
tweets_req_interval_in_secs: 60,
222234
},
235+
alert: AlertConfig {
236+
webhook_url: "https://your-webhook-url.com".to_string(),
237+
},
223238
}
224239
}
225240
}

src/db_persistence.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::repositories::raid_quest::RaidQuestRepository;
77
use crate::repositories::raid_submission::RaidSubmissionRepository;
88
use crate::repositories::relevant_tweet::RelevantTweetRepository;
99
use crate::repositories::tweet_author::TweetAuthorRepository;
10+
use crate::repositories::tweet_pull_usage::TweetPullUsageRepository;
1011
use crate::repositories::x_association::XAssociationRepository;
1112
use crate::repositories::DbResult;
1213
use crate::repositories::{
@@ -45,6 +46,7 @@ pub struct DbPersistence {
4546
pub raid_quests: RaidQuestRepository,
4647
pub raid_submissions: RaidSubmissionRepository,
4748
pub raid_leaderboards: RaidLeaderboardRepository,
49+
pub tweet_pull_usage: TweetPullUsageRepository,
4850

4951
pub pool: PgPool,
5052
}
@@ -67,6 +69,7 @@ impl DbPersistence {
6769
let raid_quests = RaidQuestRepository::new(&pool);
6870
let raid_submissions = RaidSubmissionRepository::new(&pool);
6971
let raid_leaderboards = RaidLeaderboardRepository::new(&pool);
72+
let tweet_pull_usage = TweetPullUsageRepository::new(pool.clone());
7073

7174
Ok(Self {
7275
pool,
@@ -82,6 +85,7 @@ impl DbPersistence {
8285
raid_quests,
8386
raid_submissions,
8487
raid_leaderboards,
88+
tweet_pull_usage,
8589
})
8690
}
8791

@@ -101,6 +105,7 @@ impl DbPersistence {
101105
let raid_quests = RaidQuestRepository::new(&pool);
102106
let raid_submissions = RaidSubmissionRepository::new(&pool);
103107
let raid_leaderboards = RaidLeaderboardRepository::new(&pool);
108+
let tweet_pull_usage = TweetPullUsageRepository::new(pool.clone());
104109

105110
Ok(Self {
106111
pool,
@@ -116,6 +121,7 @@ impl DbPersistence {
116121
raid_quests,
117122
raid_submissions,
118123
raid_leaderboards,
124+
tweet_pull_usage,
119125
})
120126
}
121127
}

src/errors.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,17 +74,21 @@ impl IntoResponse for AppError {
7474
AppError::Database(err) => map_db_error(err),
7575

7676
// --- Everything else ---
77-
AppError::Transaction(_)
77+
e @ (AppError::Transaction(_)
7878
| AppError::TaskGenerator(_)
7979
| AppError::Reverser(_)
8080
| AppError::Join(_)
8181
| AppError::Graphql(_)
8282
| AppError::Config(_)
8383
| AppError::Http(_)
84-
| AppError::Server(_) => (
85-
StatusCode::INTERNAL_SERVER_ERROR,
86-
"An internal server error occurred".to_string(),
87-
),
84+
| AppError::Server(_)) => {
85+
tracing::error!("Internal server error: {:?}", e.to_string());
86+
87+
(
88+
StatusCode::INTERNAL_SERVER_ERROR,
89+
"An internal server error occurred".to_string(),
90+
)
91+
}
8892
};
8993

9094
error_response(status, message)

src/http_server.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{
1414
metrics::{metrics_handler, track_metrics, Metrics},
1515
models::task::TaskStatus,
1616
routes::api_routes,
17+
services::alert_service::AlertService,
1718
Config, GraphqlClient,
1819
};
1920
use chrono::{DateTime, Utc};
@@ -29,6 +30,7 @@ pub struct AppState {
2930
pub oauth_sessions: Arc<Mutex<HashMap<String, PkceCodeVerifier>>>,
3031
pub twitter_oauth_tokens: Arc<RwLock<HashMap<String, String>>>,
3132
pub twitter_gateway: Arc<dyn TwitterGateway>,
33+
pub alert_client: Arc<AlertService>,
3234
}
3335

3436
#[derive(Debug, Clone, Serialize, Deserialize)]
@@ -115,13 +117,15 @@ pub async fn start_server(
115117
db: Arc<DbPersistence>,
116118
graphql_client: Arc<GraphqlClient>,
117119
twitter_gateway: Arc<dyn TwitterGateway>,
120+
alert_client: Arc<AlertService>,
118121
bind_address: &str,
119122
config: Arc<Config>,
120123
) -> Result<(), Box<dyn std::error::Error>> {
121124
let state = AppState {
122125
db,
123126
metrics: Arc::new(Metrics::new()),
124127
graphql_client,
128+
alert_client: alert_client,
125129
config,
126130
twitter_gateway,
127131
challenges: Arc::new(RwLock::new(HashMap::new())),

0 commit comments

Comments
 (0)