Skip to content

Commit c266b48

Browse files
committed
Upload version 27.1.0 and 27.1.1 code to repo
1 parent 3b48f0e commit c266b48

5 files changed

Lines changed: 118 additions & 32 deletions

File tree

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ 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.1.1
62+
* Update message content to work properly if an alternate award fail occurs (sent as a modmail which the subreddit can click the "contact my developer" to send to my creator)
63+
### 27.1.0
64+
* Add "Moderators and Post Authors" option to "Who can award points?"
65+
* Add notify and message setting for "Moderators and Post Authors" option
6166
### 27.0.0
6267
* Make Post Author check actually work in terms of awarding logic if "Who can award points?" is set to "Moderators, Superusers, and Post Author (OP)"
6368
* Upload profile icon (possible as of Devvit 0.12.24)

devvit.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828
"media": {
2929
"dir": "assets"
3030
},
31-
"marketingAssets": { "icon": "assets/repbot.png" },
31+
"marketingAssets": {
32+
"icon": "assets/repbot.png"
33+
},
3234
"menu": {
3335
"items": [
3436
{

src/blocks/settings.ts

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@ export enum AppSetting {
107107
NotifyOnBlockedUser = "notifyOnBlockedUser",
108108
NotifyOnTrustedUserAwardSuccess = "notifyOnTrustedUserAwardSuccess",
109109
TrustedUserAwardSuccessMessage = "trustedUserAwardSuccessMessage",
110+
ModsAndPostAuthorDisallowedMessage = "modsAndPostAuthorDisallowedMessage",
111+
NotifyOnModsAndPostAuthorDisallowed = "notifyOnModsAndPostAuthorDisallowed",
110112
}
111113

112114
export enum TemplateDefaults {
@@ -121,7 +123,7 @@ export enum TemplateDefaults {
121123
ApprovedOnlyDisallowedMessage = "Only moderators and approved users can award points.",
122124
SelfAwardMessage = "You can't award yourself a {{name}}.",
123125
BotAwardMessage = "You can't award u/{{awardee}} {{name}}s.",
124-
NotifyOnSelfAwardTemplate = "Hello {{awarder}}, you cannot award a {{name}} to yourself.",
126+
SelfAwardTemplate = "Hello {{awarder}}, you cannot award a {{name}} to yourself.",
125127
NotifyOnSuccessTemplate = "+1 {{name}} awarded to u/{{awardee}} by u/{{awarder}}. Total: {{total}}{{symbol}}. {{awardee}}'s user page is located [here]({{awardeePage}}). Leaderboard is located [here]({{leaderboard}}).",
126128
NotifyOnSuperuserTemplate = "Hello {{awardee}},\n\nNow that you have reached {{threshold}} points you can now award points yourself, even if normal users do not have permission to. Please use the command `{{command}}` if you'd like to do this.",
127129
MessageToRestrictedUsers = "***ATTENTION to OP: You must award {{name}}s by replying to the successful comments. Valid command(s) are **{{commands}}**. Failure to do so may result in a ban.***\n\n***Commenters MUST put the location in spoiler tags.***\n\n*To hide text, write it like this `>!Text goes here!<` = >!Text goes here!<. [Reddit Markdown Guide]({{markdown_guide}})*.",
@@ -138,6 +140,7 @@ export enum TemplateDefaults {
138140
PostAuthorAwardMessage = "OPs cannot be awarded points.",
139141
PointAlreadyAwardedToUserViaAltCommandMessage = "{{awardee}} has already received a {{name}} for this post.",
140142
TrustedUserAwardSuccessMessage = "Superuser u/{{awarder}} gave an award! u/{{awardee}} now has {{total}}{{symbol}} {{name}}s. {{awardee}}'s user page is located [here]({{awardeePage}}). Leaderboard is located [here]({{leaderboard}}).",
143+
ModsAndPostAuthorDisallowedMessage = "Only moderators and Post Authors (OPs) can award {{name}}s.",
141144
}
142145

143146
export enum AutoSuperuserReplyOptions {
@@ -164,6 +167,27 @@ export enum NotifyOnModOnlyDisallowedReplyOptions {
164167
ReplyAsComment = "replybycomment",
165168
}
166169

170+
export enum NotifyOnModAndPostAuthorDisallowedReplyOptions {
171+
NoReply = "none",
172+
ReplyByPM = "replybypm",
173+
ReplyAsComment = "replybycomment",
174+
}
175+
176+
const NotifyOnModAndPostAuthorDisallowedReplyOptionChoices = [
177+
{
178+
label: "No Notification",
179+
value: NotifyOnModAndPostAuthorDisallowedReplyOptions.NoReply,
180+
},
181+
{
182+
label: "Send user a private message",
183+
value: NotifyOnModAndPostAuthorDisallowedReplyOptions.ReplyByPM,
184+
},
185+
{
186+
label: "Reply as comment",
187+
value: NotifyOnModAndPostAuthorDisallowedReplyOptions.ReplyAsComment,
188+
},
189+
];
190+
167191
export enum NotifyOnPostAuthorAwardReplyOptions {
168192
NoReply = "none",
169193
ReplyByPM = "replybypm",
@@ -465,6 +489,7 @@ export enum AccessControlOptions {
465489
ModOnly = "moderators-only",
466490
ModsAndSuperusers = "moderators-and-superusers",
467491
ModsSuperusersAndPostAuthor = "moderators-superusers-and-op",
492+
ModsAndPostAuthor = "moderators-and-op",
468493
Everyone = "everyone",
469494
}
470495

@@ -481,6 +506,10 @@ const AccessControlOptionChoices = [
481506
label: "Moderators and Superusers",
482507
value: AccessControlOptions.ModsAndSuperusers,
483508
},
509+
{
510+
label: "Moderators and Post Author (OP)",
511+
value: AccessControlOptions.ModsAndPostAuthor,
512+
},
484513
{
485514
label: "Moderators, Superusers, and Post Author (OP)",
486515
value: AccessControlOptions.ModsSuperusersAndPostAuthor,
@@ -850,6 +879,25 @@ export const appSettings: SettingsFormField[] = [
850879
defaultValue: TemplateDefaults.ModOnlyDisallowedMessage,
851880
onValidate: paragraphFieldContainsText,
852881
},
882+
{
883+
type: "select",
884+
name: AppSetting.NotifyOnModsAndPostAuthorDisallowed,
885+
label: "Notify users when only moderators and the Post Author (OP) can award points",
886+
options: NotifyOnModAndPostAuthorDisallowedReplyOptionChoices,
887+
defaultValue: [
888+
NotifyOnModAndPostAuthorDisallowedReplyOptions.NoReply,
889+
],
890+
onValidate: selectFieldHasOptionChosen,
891+
},
892+
{
893+
type: "paragraph",
894+
name: AppSetting.ModsAndPostAuthorDisallowedMessage,
895+
label: "Mods and Post Author Disallowed Message",
896+
helpText:
897+
"Message shown when a user tries to award a point but only moderators and the Post Author (OP) can award points",
898+
defaultValue: TemplateDefaults.ModsAndPostAuthorDisallowedMessage,
899+
onValidate: paragraphFieldContainsText,
900+
},
853901
{
854902
type: "select",
855903
name: AppSetting.NotifyOnApprovedOnlyDisallowed,
@@ -1146,7 +1194,7 @@ export const appSettings: SettingsFormField[] = [
11461194
label: "Self Award Message",
11471195
helpText:
11481196
"Shown when someone tries to award themselves. Placeholders Supported: {{name}}, {{awarder}}",
1149-
defaultValue: TemplateDefaults.NotifyOnSelfAwardTemplate,
1197+
defaultValue: TemplateDefaults.SelfAwardTemplate,
11501198
onValidate: paragraphFieldContainsText,
11511199
},
11521200
{

src/blocks/triggers/comment/on-comment-trigger.ts

Lines changed: 55 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
import { CommentSubmit, CommentUpdate } from "@devvit/protos";
2-
import { TriggerContext, Comment, SettingsValues, User } from "@devvit/public-api";
2+
import {
3+
TriggerContext,
4+
Comment,
5+
SettingsValues,
6+
User,
7+
} from "@devvit/public-api";
38
import {
49
_replyToUser,
510
CommentTriggerContext,
@@ -19,6 +24,7 @@ import {
1924
handleAltUserAction,
2025
} from "./user-specific-logic/alt-user-action";
2126
import {
27+
AccessControlOptions,
2228
AppSetting,
2329
NotifyOnDisallowedFlairReplyOptions,
2430
NotifyOnModAwardFailReplyOptions,
@@ -135,14 +141,24 @@ export async function handleThanksEvent(
135141
logger.warn("❌ Could not fetch user object for awarder", { awarder });
136142
return;
137143
}
138-
await checkPermissionOfUser(
144+
145+
const hasPermission = await userHasPermission(
139146
event,
140147
user.id,
141148
commentTriggerContext,
142149
devvitContext,
143150
settings,
144151
);
145152

153+
if (!hasPermission) {
154+
logger.debug("❌ User does not have permission", {
155+
awarder,
156+
commentId: event.comment.id,
157+
});
158+
159+
return;
160+
}
161+
146162
// ─────────────────────────────────────────────
147163
// Detect which command type exists
148164
// ─────────────────────────────────────────────
@@ -315,7 +331,7 @@ export async function unflairedPostLogic(
315331
// 🚫 Unflaired posts not allowed
316332
if (!allowUnflairedPosts && postFlairText === "") {
317333
// 🚫 Ignore bot’s own comments to prevent loops
318-
if (event.author.name === context.appName) {
334+
if (event.author.name === context.appSlug) {
319335
logger.debug(
320336
"🤖 Bot-authored comment detected; skipping unflaired-post response",
321337
);
@@ -427,7 +443,7 @@ export async function flairTextNotAllowedLogic(
427443
return;
428444
}
429445

430-
if (event.author.name === context.appName) {
446+
if (event.author.name === context.appSlug) {
431447
// 🚫 Ignore bot’s own comments to prevent loops
432448
logger.debug(
433449
"🤖 Bot-authored comment detected; skipping disallowed flair response",
@@ -487,7 +503,7 @@ export async function selfAwardAttemptLogic(
487503
const pointName = (settings[AppSetting.PointName] as string) ?? "point";
488504
const selfMsgTemplate =
489505
(settings[AppSetting.SelfAwardMessage] as string) ??
490-
TemplateDefaults.NotifyOnSelfAwardTemplate;
506+
TemplateDefaults.SelfAwardTemplate;
491507
const notifySelf = ((settings[
492508
AppSetting.NotifyOnSelfAward
493509
] as string[]) ?? [NotifyOnSelfAwardReplyOptions.NoReply])[0];
@@ -527,7 +543,7 @@ export async function replyToUser(
527543

528544
// 🚫 Prevent bot loops
529545
if (
530-
recipient.toLowerCase() === context.appName.toLowerCase() ||
546+
recipient.toLowerCase() === context.appSlug.toLowerCase() ||
531547
recipient.toLowerCase() === "automoderator"
532548
) {
533549
logger.debug("🤖 replyToUser: recipient is bot/system — skipping");
@@ -585,32 +601,36 @@ export async function replyToUser(
585601
}
586602
}
587603

588-
export async function checkPermissionOfUser(
604+
export async function userHasPermission(
589605
event: CommentSubmit | CommentUpdate,
590606
awarderID: string,
591607
commentTriggerContext: CommentTriggerContext,
592608
devvitContext: TriggerContext,
593609
settings: SettingsValues,
594-
) {
595-
if (!event.post || !event.comment) return;
610+
): Promise<boolean> {
611+
if (!event.post || !event.comment) return false;
612+
596613
const pointName = (settings[AppSetting.PointName] as string) ?? "point";
597614

598615
const isMod = commentTriggerContext.isMod;
599616
const isSuperUser = commentTriggerContext.isSuperUser;
600617
const isAltUser = commentTriggerContext.isAltUser;
601618
const isOP = awarderID === event.post.authorId;
619+
602620
const accessControl = ((settings[AppSetting.AccessControl] as string[]) ?? [
603621
"everyone",
604622
])[0];
605623

606624
const hasPermission =
607-
accessControl === "everyone" ||
608-
(accessControl === "moderators-only" && isMod) ||
609-
(accessControl === "moderators-and-superusers" &&
625+
accessControl === AccessControlOptions.Everyone ||
626+
(accessControl === AccessControlOptions.ModOnly && isMod) ||
627+
(accessControl === AccessControlOptions.ModsAndSuperusers &&
610628
(isMod || isSuperUser)) ||
611-
(accessControl === "moderators-superusers-and-op" &&
629+
(accessControl === AccessControlOptions.ModsSuperusersAndPostAuthor &&
612630
(isMod || isSuperUser || isOP)) ||
613-
(accessControl === "alt-users-only" && isAltUser);
631+
(accessControl === AccessControlOptions.AltUsersOnly && isAltUser) ||
632+
(accessControl === AccessControlOptions.ModsAndPostAuthor &&
633+
(isMod || isOP));
614634

615635
logger.debug("Permission check", {
616636
accessControl,
@@ -626,37 +646,45 @@ export async function checkPermissionOfUser(
626646
let notifyKey: AppSetting;
627647

628648
switch (accessControl) {
629-
case "alt-users-only":
649+
case AccessControlOptions.AltUsersOnly:
630650
msgKey = AppSetting.AlternateUsersOnlyDisallowedMessage;
631651
notifyKey = AppSetting.NotifyOnAltUserDisallowed;
632652
break;
633653

634-
case "moderators-only":
654+
case AccessControlOptions.ModOnly:
635655
msgKey = AppSetting.ModOnlyDisallowedMessage;
636656
notifyKey = AppSetting.NotifyOnModOnlyDisallowed;
637657
break;
638658

639-
case "moderators-and-superusers":
659+
case AccessControlOptions.ModsAndSuperusers:
640660
msgKey = AppSetting.ApprovedOnlyDisallowedMessage;
641661
notifyKey = AppSetting.NotifyOnApprovedOnlyDisallowed;
642662
break;
643663

644-
case "moderators-superusers-and-op":
664+
case AccessControlOptions.ModsSuperusersAndPostAuthor:
645665
msgKey = AppSetting.OPOnlyDisallowedMessage;
646666
notifyKey = AppSetting.NotifyOnOPOnlyDisallowed;
647667
break;
648668

669+
case AccessControlOptions.ModsAndPostAuthor:
670+
msgKey = AppSetting.ModsAndPostAuthorDisallowedMessage;
671+
notifyKey = AppSetting.NotifyOnModsAndPostAuthorDisallowed;
672+
break;
673+
649674
default:
650675
logger.warn("⚠️ Unknown accessControl value", {
651676
accessControl,
652677
});
653-
return;
678+
return false;
654679
}
655680

656681
const denyMsg = formatMessage(
657682
(settings[msgKey] as string) ??
658683
TemplateDefaults.ModOnlyDisallowedMessage,
659-
{ awarder: awarderID, name: pointName },
684+
{
685+
awarder: awarderID,
686+
name: pointName,
687+
},
660688
);
661689

662690
const notifyMode = ((settings[notifyKey] as string[]) ?? ["none"])[0];
@@ -669,8 +697,10 @@ export async function checkPermissionOfUser(
669697
event.comment.id,
670698
);
671699

672-
return;
700+
return false;
673701
}
702+
703+
return true;
674704
}
675705

676706
export async function awarderIsBot(
@@ -681,7 +711,7 @@ export async function awarderIsBot(
681711
) {
682712
if (!event.comment) return;
683713
if (
684-
["automoderator", devvitContext.appName.toLowerCase()].includes(
714+
["automoderator", devvitContext.appSlug.toLowerCase()].includes(
685715
awarder.toLowerCase(),
686716
)
687717
) {
@@ -704,7 +734,7 @@ export async function awarderIsBot(
704734
const pointName = (settings[AppSetting.PointName] as string) ?? "point";
705735

706736
if (
707-
awarder === devvitContext.appName ||
737+
awarder === devvitContext.appSlug ||
708738
awarder.toLowerCase() === "automoderator"
709739
) {
710740
const botMsg = formatMessage(
@@ -732,7 +762,7 @@ export async function recipientIsBot(
732762
if (!event.comment) return;
733763
const pointName = (settings[AppSetting.PointName] as string) ?? "point";
734764
if (
735-
["automoderator", devvitContext.appName.toLowerCase()].includes(
765+
["automoderator", devvitContext.appSlug.toLowerCase()].includes(
736766
awarder.toLowerCase(),
737767
)
738768
) {
@@ -741,7 +771,7 @@ export async function recipientIsBot(
741771
}
742772

743773
if (
744-
["automoderator", devvitContext.appName.toLowerCase()].includes(
774+
["automoderator", devvitContext.appSlug.toLowerCase()].includes(
745775
recipient.toLowerCase(),
746776
)
747777
) {

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

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,13 @@ export async function commentContainsAltCommand(
108108
}
109109
} catch (err) {
110110
const botCreator = "ryry50583583";
111+
const subject = `Alternate%20Command%20Error%20in%20r/${event.subreddit.name}`;
111112
const message =
112-
`We encountered an error which is related to the alternate command in r/${event.subreddit.name}.\n\n` +
113-
`If you could take a look at it and provide any insights, that would be appreciated!\n\n` +
114-
`**Error details:** ${err instanceof Error ? err.stack || err.message : String(err)}`;
113+
`We%20encountered%20an%20error%20which%20is%20related%20to%20the%20alternate%20command%20in%20r/${event.subreddit.name}.%0A%0A` +
114+
`If%20you%20could%20take%20a%20look%20at%20it%20and%20provide%20any%20insights,%20that%20would%20be%20appreciated!%0A%0A` +
115+
`**Error details:**%20***${err instanceof Error ? err.stack || err.message : String(err)}***`;
115116
logger.error(
116-
`If you see this error, please [contact my developer](https://www.reddit.com/message/compose?to=${botCreator}&message=${message}). ` +
117+
`If you see this error, please [contact my developer](https://www.reddit.com/message/compose?to=${botCreator}&message=${message}&subject=${subject}). ` +
117118
`Please send the message as-is unless you have any additional information to provide.`,
118119
{},
119120
context,

0 commit comments

Comments
 (0)