Skip to content

Commit

Permalink
feat : Added the feature of sending notification to discord for any n…
Browse files Browse the repository at this point in the history
…ew video/notion
  • Loading branch information
amanbairagi30 committed Jun 14, 2024
1 parent 7232c99 commit 499d178
Show file tree
Hide file tree
Showing 9 changed files with 189 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ BOT_TOKEN = "123"
GUILD_ID = "123"
LOCAL_CMS_PROVIDER = true
CACHE_EXPIRE_S = 10

ADMINS = "Random,[email protected]"
NEXT_PUBLIC_DISABLE_FEATURES = "featurea,featureb,featurec"
NEXT_PUBLIC_DISCORD_WEBHOOK_URL=""
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,12 @@ Read our [contribution guidelines](./CONTRIBUTING.md) for more details.
</a>

## Issues on mac Silicon
brew install pkg-config cairo pango libpng jpeg giflib librsvg
brew install pkg-config cairo pango libpng jpeg giflib librsvg



## Discord Webhook Configuration

![image](https://github.com/code100x/bounty-hook/assets/118182376/6653f928-95e0-4f55-8da8-b15837c9e66f)
![image](https://github.com/code100x/bounty-hook/assets/118182376/e504b067-ba88-4a91-8947-7433d5a75419)
![image](https://github.com/code100x/bounty-hook/assets/118182376/19c1db48-cefc-46d7-964c-ca5e39c94d80)
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-avatar": "^1.0.4",
"@radix-ui/react-dialog": "^1.0.5",
"@radix-ui/react-checkbox": "^1.0.4",
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-navigation-menu": "^1.1.4",
Expand Down
2 changes: 2 additions & 0 deletions src/app/admin/content/[...courseId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,10 @@ export default async function UpdateCourseContent({
{course?.title}
<div className="font-bold md:text-5xl lg:text-6xl">Content</div>
<AddContent
rest={rest}
courseId={parseInt(courseId, 10)}
parentContentId={parseFloat(rest[rest.length - 1])}
courseTitle={course?.title}
/>
<AdminCourseContent
courseContent={courseContent.map((x: any) => ({
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/admin/content/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,5 +103,5 @@ export const POST = async (req: NextRequest) => {
});
}
}
return NextResponse.json({}, { status: 200 });
return NextResponse.json({ id: content.id }, { status: 200 });
};
40 changes: 40 additions & 0 deletions src/app/services/DiscordService.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import axios from 'axios';

interface DiscordData {
type: string;
thumbnail: string;
title: string;
courseTitle: string;
courseId: number;
mediaId: number;
currFolderId: number;
}

export default function DiscordService() {
const sendUpdateToDiscord = async (data: DiscordData) => {
const body = {
content: 'Hello @everyone',
tts: false,
color: 'white',
embeds: [
{
title: `New ${data?.type === 'notion' ? 'NOTE' : data?.type?.toUpperCase()}`,
description: `${data?.title} has been added in the ${data?.courseTitle} , [Click here to visit this ${data?.type === 'notion' ? 'note' : data?.type}](https://app.100xdevs.com/courses/${data.courseId}/${data.currFolderId}/${data.mediaId})`,
},
],
};

try {
const res = await axios.post(
`${process.env.NEXT_PUBLIC_DISCORD_WEBHOOK_URL}`,
body,
);
console.log(res);
} catch (error) {
console.log(error);
}
};
return {
sendUpdateToDiscord,
};
}
121 changes: 99 additions & 22 deletions src/components/admin/AddContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,43 +2,136 @@
import { useEffect, useState } from 'react';
import { AddNotionMetadata } from './AddNotionMetadata';
import { Input } from '../ui/input';
import { useRecoilState } from 'recoil';
import { loader } from '@/store/atoms/loader';
import DiscordService from '@/app/services/DiscordService';
import { Checkbox } from '../ui/checkbox';

export const AddContent = ({
rest,
courseId,
parentContentId,
courseTitle,
}: {
rest: string[];
courseId: number;
parentContentId?: number;
courseTitle: string;
}) => {
const [type, setType] = useState('folder');
const [imageUri, setImageUri] = useState('');
const [title, setTitle] = useState('');
const [metadata, setMetadata] = useState({});
const [checked, setChecked] = useState<Boolean>(true);
const [adminPassword, setAdminPassword] = useState('');
const [loading, setLoading] = useRecoilState(loader);

const { sendUpdateToDiscord } = DiscordService();

interface DiscordData {
type: string;
thumbnail: string;
title: string;
courseTitle: string;
courseId: number;
currFolderId: number;
mediaId: number;
}

const postOnDiscord = (data: DiscordData) => {
sendUpdateToDiscord(data);
};

const handleContentSubmit = async () => {
setLoading(true);
const response = await fetch('/api/admin/content', {
body: JSON.stringify({
type,
description: '',
thumbnail: imageUri,
title,
courseId,
parentContentId,
metadata,
adminPassword,
}),
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
setLoading(false);

const respData: { id: number } = await response.json();

const data = {
type,
thumbnail:
'https://d2szwvl7yo497w.cloudfront.net/courseThumbnails/video.png',
title,
courseTitle,
courseId,
currFolderId: parseInt(rest[0], 10),
mediaId: respData.id,
};

if (checked && response.status === 200) {
if (type === 'notion' || type === 'video') {
postOnDiscord(data);
setLoading(false);
}
} else {
setLoading(false);
}
};

return (
<div>
<div className="">
<div className="space-x-2 p-2">
<button
className={`${type === 'video' ? 'bg-green-500' : 'bg-blue-500'} rounded px-4 py-2 font-bold text-white`}
onClick={() => setType('video')}
onClick={() => {
setType('video');
setMetadata({});
}}
>
Video
</button>
<button
className={`${type === 'folder' ? 'bg-green-500' : 'bg-blue-500'} rounded px-4 py-2 font-bold text-white`}
onClick={() => setType('folder')}
onClick={() => {
setType('folder');
setMetadata({});
}}
>
Folder
</button>
<button
className={`${type === 'notion' ? 'bg-green-500' : 'bg-blue-500'} rounded px-4 py-2 font-bold text-white`}
onClick={() => setType('notion')}
onClick={() => {
setType('notion');
setMetadata({});
}}
>
Notion
</button>
</div>
{(type === 'video' || type === 'notion') && (
<div className="mt-2 flex items-center gap-2 p-2">
<Checkbox
onCheckedChange={() => setChecked((prev) => !prev)}
defaultChecked
id="discord"
/>
<label
htmlFor="discord"
className="text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
>
Send the notification to the discord as well
</label>
</div>
)}
<br /> <br />
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2 md:grid-cols-3">
<Input
Expand All @@ -63,27 +156,11 @@ export const AddContent = ({
{type === 'video' && <AddVideosMetadata onChange={setMetadata} />}
{type === 'notion' && <AddNotionMetadata onChange={setMetadata} />}
<button
onClick={async () => {
await fetch('/api/admin/content', {
body: JSON.stringify({
type,
description: '',
thumbnail: imageUri,
title,
courseId,
parentContentId,
metadata,
adminPassword,
}),
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
}}
onClick={handleContentSubmit}
disabled={loading}
className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
>
Submit
{loading ? 'Submitting' : 'Submit'}
</button>
</div>
</div>
Expand Down
30 changes: 30 additions & 0 deletions src/components/ui/checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
'use client';

import * as React from 'react';
import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
import { Check } from 'lucide-react';

import { cn } from '@/lib/utils';

const Checkbox = React.forwardRef<
React.ElementRef<typeof CheckboxPrimitive.Root>,
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
>(({ className, ...props }, ref) => (
<CheckboxPrimitive.Root
ref={ref}
className={cn(
'peer h-4 w-4 shrink-0 rounded-sm border border-primary ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground',
className,
)}
{...props}
>
<CheckboxPrimitive.Indicator
className={cn('flex items-center justify-center text-current')}
>
<Check className="h-4 w-4" />
</CheckboxPrimitive.Indicator>
</CheckboxPrimitive.Root>
));
Checkbox.displayName = CheckboxPrimitive.Root.displayName;

export { Checkbox };
6 changes: 6 additions & 0 deletions src/store/atoms/loader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { atom } from 'recoil';

export const loader = atom({
key: 'loader',
default: false,
});

0 comments on commit 499d178

Please sign in to comment.