Skip to content

Commit

Permalink
Clippy: Add commands for creating tags (#1273)
Browse files Browse the repository at this point in the history
* add tags to clippy

* add get-tag function

* fix bugs

* clean up
  • Loading branch information
andrewliu08 authored Aug 15, 2024
1 parent 0147851 commit 63fdd3a
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 20 deletions.
6 changes: 4 additions & 2 deletions apps/daimo-clippy/.env.example
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
DAIMO_API_URL='http://localhost:3000'
DAIMO_CHAIN='baseSepolia'
SLACK_COMMAND_TOKEN=''
DAIMO_API_KEY=''
SLACK_BOT_TOKEN=''
SLACK_SIGNING_SECRET=''
DAIMO_API_KEY=''
CLIPPY_TAG_UPDATE_TOKEN=''
64 changes: 64 additions & 0 deletions apps/daimo-clippy/src/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { rpc } from "./rpc";
import { getJSONblock, parseKwargs, unfurlLink } from "./utils";

const apiKey = assertNotNull(process.env.DAIMO_API_KEY);
const clippyTagUpdateToken = assertNotNull(process.env.CLIPPY_TAG_UPDATE_TOKEN);

export async function handleCommand(text: string): Promise<string> {
console.log(`[SLACK-BOT] Handling command ${text}`);
Expand Down Expand Up @@ -77,6 +78,14 @@ const commands: Record<string, Command> = {
help: "Gets the best swap quote for fromToken to toToken. Args: [fromAmount=1.23, fromToken=DAI, toToken=USDC]",
fn: getSwapQuote,
},
"get-tag": {
help: "Get the links previously associated with a tag. Args: [tag]",
fn: getTag,
},
"set-tag": {
help: "Create or update a tag. Associates https://daimo.com/l/t/<tag> with a link. Args: [tag, link, updateLink]",
fn: setTag,
},
health: {
help: "Checks that the API is up",
fn: health,
Expand Down Expand Up @@ -270,6 +279,61 @@ export async function createInviteCode(
)}`;
}

export async function getTag(kwargs: Map<string, string>): Promise<string> {
const tag = assertNotNull(kwargs.get("tag"));

console.log(`[SLACK-BOT] getting tag: ${tag}`);
const res = await rpc.getTagHistory.query({ tag });

return `Successfully got tag: ${getJSONblock(res)}`;
}

export async function setTag(kwargs: Map<string, string>) {
const tag = assertNotNull(kwargs.get("tag"));
const link = kwargs.get("link");
const updateLink = kwargs.get("updateLink");

if (!link && !updateLink) return "Must specify link or updateLink";
if (link && updateLink) return "Cannot specify both link and updateLink";

if (link) {
console.log(`[SLACK-BOT] creating tag: ${tag}, link: ${link}`);
const existingTag = await rpc.getTagRedirect.query({ tag });
if (existingTag) {
return `Tag ${tag} already exists. To update the tag, use the updateLink parameter instead of link`;
}

try {
const res = await rpc.createTagRedirect.mutate({
apiKey,
tag,
link,
updateToken: clippyTagUpdateToken,
});
return `Successfully created tag: ${getJSONblock(res)}`;
} catch (e) {
console.error(`[SLACK-BOT] Error setting tag: ${e}`);
return `Error setting tag: ${e}`;
}
} else {
console.log(`[SLACK-BOT] updating tag: ${tag}, updateLink: ${updateLink}`);
try {
const res = await rpc.updateTagRedirect.mutate({
tag,
link: updateLink!,
updateToken: clippyTagUpdateToken,
});
return `Successfully updated tag: ${getJSONblock(res)}`;
} catch (e: any) {
console.error(`[SLACK-BOT] Error updating tag: ${e}`);
if (e.message === "Tag does not exist") {
return `Tag ${tag} does not exist. To create a new tag, set the link parameter instead of updateLink`;
}
return `Error updating tag: ${e}`;
}
}
}

async function viewInviteStatus(kwargs: Map<string, string>): Promise<string> {
const url = await (async () => {
if (kwargs.has("link")) {
Expand Down
15 changes: 12 additions & 3 deletions packages/daimo-api/src/api/tagRedirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,16 @@ export async function getTagRedirect(

// Returns tag redirect URL, or null if tag does not exist.
export async function getTagRedirectHist(tag: string, db: DB) {
return db.loadTagRedirectHist(tag);
return await db.loadTagRedirectHist(tag);
}

export async function createTagRedirect(
tag: string,
link: string,
updateToken: string,
db: DB
) {
return await db.saveTagRedirect(tag, link, updateToken);
}

// Updates tag redirect URL, authenticatd by updateToken.
Expand All @@ -21,9 +30,9 @@ export async function setTagRedirect(
link: string,
updateToken: string,
db: DB
): Promise<void> {
) {
await verifyTagUpdateToken(tag, updateToken, db);
return db.saveTagRedirect(tag, link);
return await db.saveTagRedirect(tag, link);
}

export async function verifyTagUpdateToken(
Expand Down
38 changes: 28 additions & 10 deletions packages/daimo-api/src/db/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,19 +235,37 @@ export class DB {
}));
}

async saveTagRedirect(tag: string, link: string) {
async saveTagRedirect(
tag: string,
link: string,
updateToken?: string
): Promise<TagRedirectRow> {
console.log(`[DB] inserting tag redirect: ${tag} -> ${link}`);
const res = await this.pool.query(
`INSERT INTO tag_redirect (tag, link) VALUES ($1, $2)
ON CONFLICT (tag) DO UPDATE SET link = $2`,
[tag, link]
);
const queryParams = [tag, link];
let query = `INSERT INTO tag_redirect (tag, link) VALUES ($1, $2)
ON CONFLICT (tag) DO UPDATE SET link = $2
RETURNING *`;

if (updateToken !== undefined) {
query = `INSERT INTO tag_redirect (tag, link, update_token) VALUES ($1, $2, $3)
ON CONFLICT (tag) DO UPDATE SET link = $2, update_token = $3
RETURNING *`;
queryParams.push(updateToken);
}

const res = await this.pool.query(query, queryParams);

if (res.rowCount && res.rowCount > 0) {
await this.pool.query(
`INSERT INTO tag_redirect_history (tag, link) VALUES ($1, $2)`,
[tag, link]
);
let historyQuery = `INSERT INTO tag_redirect_history (tag, link) VALUES ($1, $2)`;

if (updateToken !== undefined) {
historyQuery = `INSERT INTO tag_redirect_history (tag, link, update_token) VALUES ($1, $2, $3)`;
}

await this.pool.query(historyQuery, queryParams);
}

return res.rows[0];
}

async saveOffchainAction(row: OffchainActionRow) {
Expand Down
27 changes: 22 additions & 5 deletions packages/daimo-api/src/server/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import { search } from "../api/search";
import { sendUserOpV2 } from "../api/sendUserOpV2";
import { submitWaitlist } from "../api/submitWaitlist";
import {
createTagRedirect,
getTagRedirect,
getTagRedirectHist,
setTagRedirect,
Expand Down Expand Up @@ -586,30 +587,46 @@ export function createRouter(
return profileCache.updateProfileLinks(addr, actionJSON, signature);
}),

// @deprecated, remove by 2024 Q4
getTagRedirect: publicProcedure
.input(z.object({ tag: z.string() }))
.query(async (opts) => {
const { tag } = opts.input;
return getTagRedirect(tag, db);
}),

// @deprecated, remove by 2024 Q4
createTagRedirect: publicProcedure
.input(
z.object({
apiKey: z.string(),
tag: z.string(),
link: z.string(),
updateToken: z.string(),
})
)
.mutation(async (opts) => {
const { apiKey, tag, link, updateToken } = opts.input;
authorize(apiKey);

const res = await createTagRedirect(tag, link, updateToken, db);
return { tag: res.tag, link: res.link };
}),

updateTagRedirect: publicProcedure
.input(
z.object({ tag: z.string(), link: z.string(), updateToken: z.string() })
)
.mutation(async (opts) => {
const { tag, link, updateToken } = opts.input;
return setTagRedirect(tag, link, updateToken, db);

const res = await setTagRedirect(tag, link, updateToken, db);
return { tag: res.tag, link: res.link };
}),

// @deprecated, remove by 2024 Q4
getTagHistory: publicProcedure
.input(z.object({ tag: z.string() }))
.query(async (opts) => {
const { tag } = opts.input;
return getTagRedirectHist(tag, db);
return await getTagRedirectHist(tag, db);
}),

// @deprecated, remove by 2024 Q4
Expand Down

0 comments on commit 63fdd3a

Please sign in to comment.