Skip to content

Commit e034f68

Browse files
committed
Add manual username entry (only available in burger menu of main subreddit page (ie not on a post or comment)) and make entry for flair management option (ie "enabled"/"disabled") case-insensitive
1 parent 8c64565 commit e034f68

6 files changed

Lines changed: 117 additions & 18 deletions

File tree

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ This app is open source and licensed under the BSD 3-Clause License. You can fin
5858
NOTE: If you remove the app from your subreddit, it will delete all data and you will have to manually restore it to users.
5959

6060
## Version History
61+
### 27.3.0
62+
* Add manual username entry (only available in burger menu of main subreddit page (ie not on a post or comment))
63+
* Make entry for flair management option (ie "enabled"/"disabled") case-insensitive
6164
### 27.2.0
6265
* Allow mods to set whether their own flair or others' should be edited by the bot when they receive an award
6366
* Allow mods to check whether a user has this toggle enabled or not

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "therepbot",
4-
"version": "27.2.0",
4+
"version": "27.3.0",
55
"license": "BSD-3-Clause",
66
"type": "module",
77
"scripts": {

src/blocks/main.ts

Lines changed: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,12 +99,23 @@ export const manualSetFlairManagementForm = Devvit.createForm(
9999
(data) => ({ fields: data.fields as FormField[] }),
100100
manualSetFlairManagementFormHandler,
101101
);
102+
export const manualSetFlairManagementForUserForm = Devvit.createForm(
103+
(data) => ({ fields: data.fields as FormField[] }),
104+
manualSetFlairManagementForUserFormHandler,
105+
);
102106

103107
export const manualPostRestrictionRemovalForm = Devvit.createForm(
104108
(data) => ({ fields: data.fields as FormField[] }),
105109
manualPostRestrictionRemovalHandler,
106110
);
107111

112+
Devvit.addMenuItem({
113+
label: "[RepBot] - Set Flair Management For Specified User",
114+
forUserType: "moderator",
115+
location: "subreddit",
116+
onPress: handleFlairToggleForUser,
117+
});
118+
108119
Devvit.addMenuItem({
109120
label: "[RepBot] - Remove post restriction from user",
110121
forUserType: "moderator",
@@ -293,7 +304,7 @@ export async function handleFlairToggle(
293304
label: `Flair management for u/${username}`,
294305
defaultValue: currentValue,
295306
helpText:
296-
"enabled = bot manages flair, disabled = bot will not manage flair",
307+
"Case insensitive. 'enabled' = bot manages flair, 'disabled' = bot will not manage flair",
297308
required: true,
298309
},
299310
];
@@ -364,8 +375,13 @@ export async function manualSetFlairManagementFormHandler(
364375
context: Context,
365376
) {
366377
const value = event.values.isEnabled as string | undefined;
367-
368-
if (value !== "enabled" && value !== "disabled") {
378+
if (!value) {
379+
context.ui.showToast("Your entry must contain a value.");
380+
return;
381+
}
382+
const enabled = /^enabled$/i;
383+
const disabled = /^disabled$/i;
384+
if (disabled.test(value) && !enabled.test(value)) {
369385
context.ui.showToast(`You must enter "enabled" or "disabled"`);
370386
return;
371387
}
@@ -375,7 +391,9 @@ export async function manualSetFlairManagementFormHandler(
375391

376392
try {
377393
if (context.commentId) {
378-
const comment = await context.reddit.getCommentById(context.commentId);
394+
const comment = await context.reddit.getCommentById(
395+
context.commentId,
396+
);
379397
username = comment?.authorName ?? null;
380398
} else if (context.postId) {
381399
const post = await context.reddit.getPostById(context.postId);
@@ -401,7 +419,7 @@ export async function manualSetFlairManagementFormHandler(
401419
const key = `flairToggle:${user.username}`;
402420

403421
// enabled = no key
404-
if (value === "enabled") {
422+
if (enabled.test(value)) {
405423
await context.redis.del(key);
406424
} else {
407425
await context.redis.set(key, "disabled");
@@ -412,6 +430,93 @@ export async function manualSetFlairManagementFormHandler(
412430
);
413431
}
414432

433+
export async function handleFlairToggleForUser(
434+
_: MenuItemOnPressEvent,
435+
context: Context,
436+
) {
437+
try {
438+
const fields = [
439+
{
440+
name: "target",
441+
type: "string",
442+
label: "Target Username (no u/)",
443+
defaultValue: "",
444+
helpText:
445+
"Case insensitive. The username of the user you want to toggle flair management for",
446+
required: true,
447+
},
448+
{
449+
name: "isEnabled",
450+
type: "string",
451+
label: `Set Flair Management Status`,
452+
defaultValue: "",
453+
helpText:
454+
"Case insensitive. 'enabled' = bot manages flair, 'disabled' = bot will not manage flair",
455+
required: true,
456+
},
457+
];
458+
459+
context.ui.showForm(manualSetFlairManagementForUserForm, { fields });
460+
} catch (err) {
461+
logger.error("❌ Failed to toggle flair management", {
462+
error: String(err),
463+
});
464+
465+
context.ui.showToast(
466+
"An error occurred while toggling flair management.",
467+
);
468+
}
469+
}
470+
471+
export async function manualSetFlairManagementForUserFormHandler(
472+
event: FormOnSubmitEvent<JSONObject>,
473+
context: Context,
474+
) {
475+
const isEnabled = event.values.isEnabled as string | undefined;
476+
const target = event.values.target as string | undefined;
477+
478+
if (!isEnabled) {
479+
context.ui.showToast("Enablement status is required.");
480+
return;
481+
}
482+
483+
if (!target) {
484+
context.ui.showToast("Target user is required.");
485+
return;
486+
}
487+
488+
const enabled = /^enabled$/i;
489+
const disabled = /^disabled$/i;
490+
491+
if (!disabled.test(isEnabled) && !enabled.test(isEnabled)) {
492+
context.ui.showToast(`You must enter "enabled" or "disabled"`);
493+
return;
494+
}
495+
496+
const userRegex = /^[a-z0-9\_\-]{3,21}$/i;
497+
498+
if (!userRegex.test(target)) {
499+
context.ui.showToast(
500+
"Username must be between 3 and 21 characters long and contain only letters, numbers, underscores, or hyphens.",
501+
);
502+
return;
503+
}
504+
505+
// 🔍 Resolve user from target
506+
const key = `flairToggle:${target}`;
507+
508+
// enabled = no key
509+
if (enabled.test(isEnabled)) {
510+
await context.redis.del(key);
511+
} else {
512+
await context.redis.set(key, "disabled");
513+
}
514+
515+
context.ui.showToast(
516+
`Flair management for u/${target} is now ${isEnabled}`,
517+
);
518+
}
519+
415520
Devvit.configure({
416521
redditAPI: true,
417522
redis: true,

src/blocks/settings.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ export enum AppSetting {
7272
OPOnlyDisallowedMessage = "opOnlyDisallowedMessage",
7373
NotifyOnDuplicateAward = "notifyOnDuplicateAward",
7474
NotifyOnBotAward = "notifyOnBotAward",
75-
NotifyOnApprove = "notifyOnApprove",
7675
NotifyOnModOnlyDisallowed = "notifyOnModOnlyDisallowed",
7776
NotifyOnApprovedOnlyDisallowed = "notifyOnApprovedOnlyDisallowed",
7877
NotifyOnOPOnlyDisallowed = "notifyOnOPOnlyDisallowed",
@@ -1264,14 +1263,6 @@ export const appSettings: SettingsFormField[] = [
12641263
defaultValue: TemplateDefaults.BotAwardMessage,
12651264
onValidate: paragraphFieldContainsText,
12661265
},
1267-
{
1268-
type: "select",
1269-
name: AppSetting.NotifyOnApprove,
1270-
label: "Notify a user when a point is awarded by a moderator",
1271-
options: NotifyOnModApproveReplyOptionChoices,
1272-
defaultValue: [NotifyOnModApproveReplyOptions.NoReply],
1273-
onValidate: selectFieldHasOptionChosen,
1274-
},
12751266
],
12761267
},
12771268
{

src/blocks/triggers/comment/user-specific-logic/alt-user-action.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export async function commentContainsAltCommand(
8282
const body = event.comment.body ?? "";
8383

8484
// Fetch commands
85-
const triggers = await getTriggers(context); // e.g., "!mod"
85+
const triggers = await getTriggers(context);
8686
for (const trigger of triggers) {
8787
if (
8888
!new RegExp(

0 commit comments

Comments
 (0)