From 599bd7000984304fa0cc08e4aa24a53b83e7ab21 Mon Sep 17 00:00:00 2001 From: Nick Gerakines <12125+ngerakines@users.noreply.github.com> Date: Mon, 11 Nov 2024 19:38:09 +0000 Subject: [PATCH] feature: score can be incremented Signed-off-by: Nick Gerakines <12125+ngerakines@users.noreply.github.com> --- docs/playbook-rhai.md | 45 ++++++++++++++++++++++++++------------------- etc/rhai_ngerakines_activity.rhai | 7 +++++-- src/consumer.rs | 12 +++++++++--- src/matcher.rs | 15 +++++++++------ src/storage.rs | 33 +++++++++++++++++++++++++++++++-- testdata/rhai_match_everything.rhai | 2 +- testdata/rhai_match_liked.rhai | 19 ------------------- 7 file(s) changed, 81 insertion(s)(+), 52 deletion(s)(-) diff --git a/docs/playbook-rhai.md b/docs/playbook-rhai.md --- a/docs/playbook-rhai.md +++ b/docs/playbook-rhai.md @@ -20,16 +20,19 @@ ``` ## Scripting -Rhai matchers evaluate a script that returns a `MatcherResult` object. The script must return an object that matches this type. +Rhai matchers evaluate a script that returns a `Match` object or a `string` containing the AT-URI of the post that has matched. Return values of `false` or `0` are considered not matched. -The `new_matcher_result()` function is available to create a new `MatcherResult` object. +The `upsert_match(aturi)` function is available to create a new `Match` object. It has one parameter, the AT-URI of the post that is matched. ```rhai -let result = new_matcher_result(); - +let condition_thing = true; // do some stuff ... -result +if condition_thing { + return upsert_match(); +} + +false ``` ## Provided Methods @@ -54,29 +57,33 @@ - uri: "at://did:plc:decafbad/app.bsky.feed.generator/Dcuz0bZP1" name: "rhai'ya doing" description: "This feed uses the rhai matcher to match against a complex expression." matchers: - - source: "/opt/supercell/rhaiyadoin.rhai" + - script: "/opt/supercell/rhaiyadoin.rhai" type: rhai ``` An example rhai script: ```rhai -let result = new_matcher_result(); +// Only match events from the bsky feed where the did is "did:plc:cbkjy5n7bk3ax2wplmtjofq2" (@ngerakines.me). +if event.did != "did:plc:cbkjy5n7bk3ax2wplmtjofq2" { + return false; +} +// If the event has a commit that has a record that has a $type, set rtype. Otherwise the value will be (). let rtype = event?.commit?.record["$type"]; - -if rtype != "app.bsky.feed.post" { - return result; -} - -let root_uri = event?.commit?.record?.reply?.root?.uri; - -result.matched = `${root_uri}`.starts_with("at://did:plc:cbkjy5n7bk3ax2wplmtjofq2/app.bsky.feed.post/"); - -if result.matched { - result.aturi = build_aturi(event); +switch rtype { + "app.bsky.feed.post" => { + // Compose the at-uri of the post that has matched. + return build_aturi(event); + } + "app.bsky.feed.like" => { + // Returns the subject uri of the like event or false if it doesn't exist. + return event?.commit?.record?.subject?.uri ?? false; + } + _ => { } } -result +// Nothing else matches +false ``` diff --git a/etc/rhai_ngerakines_activity.rhai b/etc/rhai_ngerakines_activity.rhai --- a/etc/rhai_ngerakines_activity.rhai +++ b/etc/rhai_ngerakines_activity.rhai @@ -2,13 +2,16 @@ if event.did != "did:plc:cbkjy5n7bk3ax2wplmtjofq2" { return false; } -let rtype = event?.commit?.record["$type"]; +let rtype = event?.commit?.record?["$type"]; switch rtype { "app.bsky.feed.post" => { return build_aturi(event); } "app.bsky.feed.like" => { - return event?.commit?.record?.subject?.uri ?? false; + let like_uri = event?.commit?.record?.subject?.uri ?? ""; + if like_uri.starts_with("at://") { + return update_match(like_uri); + } } _ => { } } diff --git a/src/consumer.rs b/src/consumer.rs --- a/src/consumer.rs +++ b/src/consumer.rs @@ -16,7 +16,8 @@ use crate::matcher::MatchOperation; use crate::storage; use crate::storage::consumer_control_get; use crate::storage::consumer_control_insert; -use crate::storage::feed_content_insert; +use crate::storage::feed_content_update; +use crate::storage::feed_content_upsert; use crate::storage::StoragePool; const MAX_MESSAGE_SIZE: usize = 25000; @@ -186,8 +187,13 @@ uri: aturi, indexed_at: event.clone().time_us, score: 1, }; - if op == MatchOperation::Upsert { - feed_content_insert(&self.pool, &feed_content).await?; + match op { + MatchOperation::Upsert => { + feed_content_upsert(&self.pool, &feed_content).await?; + }, + MatchOperation::Update => { + feed_content_update(&self.pool, &feed_content).await?; + }, } } diff --git a/src/matcher.rs b/src/matcher.rs --- a/src/matcher.rs +++ b/src/matcher.rs @@ -2,10 +2,7 @@ use anyhow::{anyhow, Context, Result}; use serde_json_path::JsonPath; -use rhai::{ - serde::to_dynamic, - CustomType, Dynamic, Engine, Scope, TypeBuilder, AST, -}; +use rhai::{serde::to_dynamic, CustomType, Dynamic, Engine, Scope, TypeBuilder, AST}; use std::{collections::HashMap, path::PathBuf, str::FromStr}; use crate::config; @@ -23,6 +20,9 @@ impl Match { fn upsert(aturi: &str) -> Self { Self(MatchOperation::Upsert, aturi.to_string()) + } + fn update(aturi: &str) -> Self { + Self(MatchOperation::Update, aturi.to_string()) } } @@ -358,7 +358,8 @@ let mut engine = Engine::new(); engine .build_type::() .register_fn("build_aturi", build_aturi) - .register_fn("new_match", Match::upsert); + .register_fn("update_match", Match::update) + .register_fn("upsert_match", Match::upsert); let ast = engine .compile_file(PathBuf::from_str(source)?) .context("cannot compile script")?; @@ -380,7 +381,9 @@ } if let Some(match_value) = value.try_cast::() { return Ok(Some(match_value)); } - Err(anyhow!("unsupported return value type: must be int, string, or match")) + Err(anyhow!( + "unsupported return value type: must be int, string, or match" + )) } impl Matcher for RhaiMatcher { diff --git a/src/storage.rs b/src/storage.rs --- a/src/storage.rs +++ b/src/storage.rs @@ -19,11 +19,11 @@ pub score: i32, } } -pub async fn feed_content_insert(pool: &StoragePool, feed_content: &FeedContent) -> Result<()> { +pub async fn feed_content_upsert(pool: &StoragePool, feed_content: &FeedContent) -> Result<()> { let mut tx = pool.begin().await.context("failed to begin transaction")?; let now = Utc::now(); - sqlx::query("INSERT OR REPLACE INTO feed_content (feed_id, uri, indexed_at, updated_at, score) VALUES (?, ?, ?, ?, ?)") + let res = sqlx::query("INSERT OR REPLACE INTO feed_content (feed_id, uri, indexed_at, updated_at, score) VALUES (?, ?, ?, ?, ?)") .bind(&feed_content.feed_id) .bind(&feed_content.uri) .bind(feed_content.indexed_at) @@ -31,6 +31,35 @@ .bind(now) .bind(feed_content.score) .execute(tx.as_mut()) .await.context("failed to insert feed content record")?; + + if res.rows_affected() == 0 { + sqlx::query("UPDATE feed_content SET score = score + ?, updated_at = ? WHERE feed_id = ? AND uri = ?") + .bind(feed_content.score) + .bind(now) + .bind(&feed_content.feed_id) + .bind(&feed_content.uri) + .execute(tx.as_mut()) + .await + .context("failed to update feed content record")?; + } + + tx.commit().await.context("failed to commit transaction") +} + +pub async fn feed_content_update(pool: &StoragePool, feed_content: &FeedContent) -> Result<()> { + let mut tx = pool.begin().await.context("failed to begin transaction")?; + + let now = Utc::now(); + sqlx::query( + "UPDATE feed_content SET score = score + ?, updated_at = ? WHERE feed_id = ? AND uri = ?", + ) + .bind(feed_content.score) + .bind(now) + .bind(&feed_content.feed_id) + .bind(&feed_content.uri) + .execute(tx.as_mut()) + .await + .context("failed to update feed content record")?; tx.commit().await.context("failed to commit transaction") } diff --git a/testdata/rhai_match_everything.rhai b/testdata/rhai_match_everything.rhai --- a/testdata/rhai_match_everything.rhai +++ b/testdata/rhai_match_everything.rhai @@ -1,2 +1,2 @@ let aturi = build_aturi(event); -return new_match(aturi); +return upsert_match(aturi); diff --git a/testdata/rhai_match_liked.rhai b/testdata/rhai_match_liked.rhai deleted file mode 100644 --- a/testdata/rhai_match_liked.rhai +++ /dev/null @@ -1,19 +0,0 @@ - -let result = new_matcher_result(); - -let rtype = event?.commit?.record["$type"]; - -switch rtype { - "app.bsky.feed.like" => { - result.matched = true; - } - // noop - _ => { } -} - -if result.matched { - result.aturi = build_aturi(event); -} - - -result -- tangled.sh