feat: add store helpers and seed r/Flipping + r/cscareerquestions comment campaigns

This commit is contained in:
pyr0ball 2026-04-27 11:00:11 -07:00
parent 9248410cf1
commit a06582c028
2 changed files with 139 additions and 0 deletions

View file

@ -97,6 +97,27 @@ class Store:
(name, product, platform, cron_schedule, notes, type),
)
def get_or_create_campaign(
self,
name: str,
product: str,
platform: str = "reddit",
type: str = "reddit_post",
cron_schedule: str | None = None,
) -> dict:
existing = self._fetchone(
"SELECT * FROM campaigns WHERE name = ? AND product = ?", (name, product)
)
if existing:
return dict(existing)
return self.create_campaign(
name=name,
product=product,
platform=platform,
type=type,
cron_schedule=cron_schedule,
)
def update_campaign(self, campaign_id: int, **fields) -> dict | None:
allowed = {"name", "product", "cron_schedule", "active", "notes"}
updates = {k: v for k, v in fields.items() if k in allowed}
@ -152,6 +173,31 @@ class Store:
(campaign_id, sub_pattern, title, body, flair, notes),
)
def upsert_variant(
self,
campaign_id: int,
sub_pattern: str,
title: str,
body: str,
flair: str | None = None,
) -> dict:
existing = self._fetchone(
"SELECT * FROM campaign_variants WHERE campaign_id = ? AND sub_pattern = ?",
(campaign_id, sub_pattern),
)
if existing:
self.conn.execute(
"UPDATE campaign_variants SET title=?, body=?, flair=? WHERE id=?",
(title, body, flair, existing["id"]),
)
self.conn.commit()
return self._fetchone("SELECT * FROM campaign_variants WHERE id=?", (existing["id"],))
return self._insert_returning(
"INSERT INTO campaign_variants (campaign_id, sub_pattern, title, body, flair)"
" VALUES (?,?,?,?,?) RETURNING *",
(campaign_id, sub_pattern, title, body, flair),
)
def update_variant(self, variant_id: int, **fields) -> dict | None:
allowed = {"sub_pattern", "title", "body", "flair", "notes"}
updates = {k: v for k, v in fields.items() if k in allowed}
@ -187,6 +233,32 @@ class Store:
(campaign_id, sub, sort_order),
)
def upsert_campaign_sub(
self,
campaign_id: int,
sub: str,
sort_order: int = 0,
thread_title_pattern: str | None = None,
thread_url_override: str | None = None,
occurrence: str | None = None,
) -> dict:
self.conn.execute(
"""INSERT INTO campaign_subs
(campaign_id, sub, sort_order, thread_title_pattern, thread_url_override, occurrence)
VALUES (?, ?, ?, ?, ?, ?)
ON CONFLICT(campaign_id, sub) DO UPDATE SET
sort_order = excluded.sort_order,
thread_title_pattern = excluded.thread_title_pattern,
thread_url_override = excluded.thread_url_override,
occurrence = excluded.occurrence""",
(campaign_id, sub, sort_order, thread_title_pattern, thread_url_override, occurrence),
)
self.conn.commit()
return self._fetchone(
"SELECT * FROM campaign_subs WHERE campaign_id = ? AND sub = ?",
(campaign_id, sub),
)
def remove_campaign_sub(self, campaign_id: int, sub: str) -> bool:
cur = self.conn.execute(
"DELETE FROM campaign_subs WHERE campaign_id = ? AND sub = ?", (campaign_id, sub)

View file

@ -342,6 +342,73 @@ def seed(store: Store) -> None:
store.create_variant(campaign_id=cid, **v)
print(f" variant: {v['sub_pattern']!r}")
# --- r/Flipping Sunday self-promo (Snipe) ---
flipping_campaign = store.get_or_create_campaign(
name="Snipe | Sunday self-promo — r/Flipping",
product="snipe",
platform="reddit",
type="reddit_comment",
cron_schedule="0 16 * * 0", # every Sunday 16:00 UTC
)
flipping_status = "skip" if flipping_campaign["name"] in existing_names else "+"
print(f" [{flipping_status}] campaign {flipping_campaign['id']}: {flipping_campaign['name']!r}")
store.upsert_campaign_sub(
campaign_id=flipping_campaign["id"],
sub="Flipping",
sort_order=0,
thread_title_pattern="Weekly Self-Promotion",
occurrence="every",
)
print(" sub: r/Flipping (thread_title_pattern='Weekly Self-Promotion', occurrence='every')")
store.upsert_variant(
campaign_id=flipping_campaign["id"],
sub_pattern="*",
title="",
body=(
"Working on evaluating auction listings? I built **Snipe** — "
"a trust-scoring tool for eBay and estate auction platforms. "
"It checks seller history, flags marketing photos, and scores "
"listings before you bid.\n\n"
"Still in beta, free to try: https://circuitforge.tech\n\n"
"Happy to answer questions about how it works."
),
)
print(" variant: '*'")
# --- r/cscareerquestions first-Sunday megathread (Peregrine) ---
cscq_campaign = store.get_or_create_campaign(
name="Peregrine | First-Sunday megathread — r/cscareerquestions",
product="peregrine",
platform="reddit",
type="reddit_comment",
cron_schedule="0 16 * * 0", # every Sunday 16:00 UTC; occurrence gates to first_sunday
)
cscq_status = "skip" if cscq_campaign["name"] in existing_names else "+"
print(f" [{cscq_status}] campaign {cscq_campaign['id']}: {cscq_campaign['name']!r}")
store.upsert_campaign_sub(
campaign_id=cscq_campaign["id"],
sub="cscareerquestions",
sort_order=0,
thread_title_pattern="Monthly Resume",
occurrence="first_sunday",
)
print(" sub: r/cscareerquestions (thread_title_pattern='Monthly Resume', occurrence='first_sunday')")
store.upsert_variant(
campaign_id=cscq_campaign["id"],
sub_pattern="*",
title="",
body=(
"I'm building **Peregrine** — a local-first job search assistant "
"for neurodivergent and adaptive-needs folks. It helps with "
"cover letters, interview prep, and tracking applications without "
"your data leaving your machine.\n\n"
"Free tier available: https://circuitforge.tech/peregrine\n\n"
"Built by someone who's been through the grind — genuinely trying "
"to make this less awful."
),
)
print(" variant: '*'")
print()
print("Seeding sub rules...")
for rule in SUB_RULES: