Slack's live, once again (#642)

* resolve public channels by id

* add live slack messages
hackers, rejoyce!

* fix redirect because of slash

* add live indicator

* block bot-spam
This commit is contained in:
Charalampos Fanoulis 2022-12-18 22:31:11 +02:00 committed by GitHub
parent 2561c4ac11
commit ed28fd2bde
5 changed files with 89 additions and 97 deletions

View file

@ -1,17 +1,9 @@
import React, { useState, useMemo, useRef, useEffect } from 'react'
import { sample, take } from 'lodash'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { Slide } from 'react-reveal'
import useWebSocket from 'react-use-websocket'
import { Box, Text } from 'theme-ui'
import { take, sample } from 'lodash'
import { Slide } from 'react-reveal'
const types = {
user_typing: 'typing',
reaction_added: 'reaction',
unmarshalling_error: 'message',
message: 'message'
}
const emoji = ['🚀', '🥳', '😂', '💖', '👀', '👍', '🙌', '🙂', '👏']
const colors = ['red', 'orange', 'yellow', 'green', 'cyan', 'blue', '#8067c3']
const Channel = ({ color, channel }) => (
@ -21,47 +13,14 @@ const Channel = ({ color, channel }) => (
)
const whitelistedChannels = new Set(
`
3d-printing ai all-hands apple art bank blockchain books cats
challenges code college-apps confessions cooking coronavirus counttoamillion deals
debate design dogs ethical-hacking flutter film food
functional gamedev gh go go-bears hack-night hackathons hardware
homelab hours hq india javascript languages late-night-hw-club lgbtq linux lounge
mason math memes minecraft music neuroscience photography python
ricing rust scrapbook ship sink-my-ship sleep social studycorner support swift
todayilearned politics welcome westborough wip workshops writing
`
`lounge counttoamillion code scrapbook hq epoch ship welcome confessions lobby question-of-the-day hack-night announcements assemble community leaders bank college-apps cdn present sprig hackathons 8-ball hackathon-organizers zrl-land design ishan-ded apple out-of-context amas batcave matthews-brain-dump memes epoch-bts poll-of-the-day pasture music too-much-information corgi-states-of-hugo gamedev denio-den india linux neighbourhood packages politics sarthaks-dreams fayd-lives-here bayarea reesericdotci celesticide minecraft cabin -코트니- neon replit-embassy the-democratic-peoples-republic-of-yishun scrapbook-dev speedy-diffusion 10-days-in-public orpheus-legion hq-surroundings annοuncements productivity hackathon-grants secret-santa ian-things-ᴛᴍ emojibot hardware-party koggy-woggy surroundings honest-impressions charlie confessions-meta carot-dev wordle pranavs-lair crypto adventofcode cake one-north mayhaps epoch-food-n-fun toby-shenanigans seattle math fishhead lgbtq chars-corner github-embassy project-ideas rant-about-high-school orpheus-show shoutout rust alumni slack-themes 3d-printing art hacktoberfest newsletter india swim`
.split(/\s+/gi)
.filter(i => i.length > 0)
.map(i => '#' + i)
)
const generateEvent = () => ({
type: sample(['message', 'typing']),
color: sample(colors),
channel: sample(Array.from(whitelistedChannels)),
timestamp: new Date().toISOString()
})
const SlackEvents = ({ sx, color, textColor, ...props }) => {
const didUnmount = useRef(false)
const [events, setEvents] = useState([])
function createMockEvents() {
setEvents(e => [generateEvent(), ...e])
setTimeout(() => createMockEvents(), 10000)
}
useEffect(() => {
setEvents([
generateEvent(),
generateEvent(),
generateEvent(),
generateEvent(),
generateEvent(),
generateEvent(),
generateEvent()
])
setTimeout(() => createMockEvents(), 5000)
}, [])
const STATIC_OPTIONS = useMemo(
() => ({
@ -70,30 +29,47 @@ const SlackEvents = ({ sx, color, textColor, ...props }) => {
}),
[]
)
/* const { lastMessage } = useWebSocket(
'wss://streambot-hackclub.herokuapp.com/',
const { lastJsonMessage } = useWebSocket(
'wss://joebunyan.haas.hackclub.com/stream',
STATIC_OPTIONS
)
useEffect(() => {
let e = lastMessage?.data
if (e) {
try {
e = JSON.parse(e)
try {
async function resolveEvent() {
if (
Object.keys(types).includes(e.type) &&
whitelistedChannels.has(e.channel)
!lastJsonMessage ||
!lastJsonMessage.type === 'message' ||
!lastJsonMessage.channel
) {
e.type = types[e.type]
e.color = sample(colors)
if (e.type === 'reaction') e.emoji = sample(emoji)
setEvents(prev => [e, ...prev])
return false
}
const { name } = await fetch(
`/api/channels/resolve/?id=${lastJsonMessage.channel}`
)
.then(r => r.json())
.catch(err => console.log(err))
if (whitelistedChannels.has(name)) {
//this check should happen before the web req, to save on net resources
setEvents(prev => [
{
type: lastJsonMessage.type,
channel: `#${name}`,
color: sample(colors)
},
...prev
])
}
} catch (err) {
true
}
resolveEvent()
} catch (err) {
true
}
}, [lastMessage]) */
}, [lastJsonMessage])
useEffect(() => {
return () => {
@ -136,22 +112,12 @@ const SlackEvents = ({ sx, color, textColor, ...props }) => {
aria-hidden="true"
{...props}
>
{take(events, 7).map(({ timestamp, type, emoji, ...channel }) => (
<Slide top duration={256} key={timestamp + JSON.stringify(channel)}>
{take(events, 7).map(({ type, channel, color }) => (
<Slide top duration={256} key={type + channel + color}>
<>
{type === 'message' && (
<>
Message in <Channel {...channel} />
</>
)}
{type === 'typing' && (
<>
typing in <Channel {...channel} />
</>
)}
{type === 'reaction' && (
<>
<Channel {...channel} /> reaction: {emoji}
Message in <Channel channel={channel} color={color} />
</>
)}
</>
@ -162,14 +128,3 @@ const SlackEvents = ({ sx, color, textColor, ...props }) => {
}
export default SlackEvents
// `
// 10-days-in-public amas assemble all-hands apple art bank books ib
// challenges code college-apps confessions cooking community coronavirus counttoamillion deals
// debate design the-democratic-republic-of-yishun dogs ethical-hacking epoch epoch-bts flutter film food
// gamedev gh go go-bears hack-night hackathons hardware
// homelab hours hq india javascript languages late-night-hw-club leaders lgbtq linux lounge
// mayhaps memes minecraft music neuroscience photography python orpheus-podcast
// rust scrapbook ship sink-my-ship sleep social studycorner support swift swim
// politics newsletter surroundings sprig hackathon-organizers hq hq-surroundings
// `

View file

@ -54,7 +54,7 @@
"react-ticker": "^1.3.2",
"react-tooltip": "^4.5.1",
"react-tsparticles": "^2.5.3",
"react-use-websocket": "3.0.0",
"react-use-websocket": "^4.2.0",
"recharts": "2.1.12",
"styled-components": "^5.3.6",
"swr": "^1.3.0",

View file

@ -0,0 +1,24 @@
export default async function handler(req, res) {
// get a public channel name by id
const channelDataReq = await fetch(
`https://slack.com/api/conversations.info?channel=${req.query.id}`,
{
headers: {
Authorization: `Bearer ${process.env.SLACK_BOT_TOKEN}`
}
}
)
if (!channelDataReq.ok) {
console.log(await channelDataReq.text())
return res.status(503).end()
}
const channelData = await channelDataReq.json()
if (!channelData.ok) {
console.log(channelData)
return res.status(400).end()
}
res.status(200).send({ name: channelData.channel.name })
}

View file

@ -1,18 +1,18 @@
import { Badge, Box, Card, Container, Heading, Grid, Text } from 'theme-ui'
import { keyframes } from '@emotion/react'
import Meta from '@hackclub/meta'
import Head from 'next/head'
import NextLink from 'next/link'
import Meta from '@hackclub/meta'
import Nav from '../components/nav'
import useSWR from 'swr'
import { Badge, Box, Card, Container, Grid, Heading, Text } from 'theme-ui'
import Footer from '../components/footer'
import ForceTheme from '../components/force-theme'
import Icon from '../components/icon'
import Stat from '../components/stat'
import Footer from '../components/footer'
import Nav from '../components/nav'
import Header from '../components/slack/header'
import SlackEvents from '../components/slack/slack-events'
import { formatted, thousands } from '../lib/members'
import useSWR from 'swr'
import Stat from '../components/stat'
import fetcher from '../lib/fetcher'
import { formatted, thousands } from '../lib/members'
const zoomSlide = keyframes({
from: { backgroundPosition: '-32px bottom' },
@ -139,13 +139,26 @@ const SlackPage = () => {
variant="subheadline"
sx={{
mt: 0,
mb: 0,
color: 'red',
textTransform: 'uppercase',
letterSpacing: 'headline'
}}
>
Live from our&nbsp;Slack
Live from our&nbsp;Slack <br />
</Heading>
<Text
as="p"
variant="caption"
sx={{
fontSize: 1,
fontWeight: 300,
fontStyle: 'italic',
mb: '16px'
}}
>
Waiting for more messages...
</Text>
<SlackEvents />
</Box>
<NextLink href="/ship" passHref>

View file

@ -3956,10 +3956,10 @@ react-tsparticles@^2.5.3:
fast-deep-equal "^3.1.3"
tsparticles-engine "^2.5.2"
react-use-websocket@3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/react-use-websocket/-/react-use-websocket-3.0.0.tgz"
integrity sha512-BInlbhXYrODBPKIplDAmI0J1VPM+1KhCLN09o+dzgQ8qMyrYs4t5kEYmCrTqyRuMTmpahylHFZWQXpfYyDkqOw==
react-use-websocket@^4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/react-use-websocket/-/react-use-websocket-4.2.0.tgz#658c4e44ee5e0fc3d29a66c2a753a72754a5db87"
integrity sha512-ZovaTlc/tWX6a590fi3kMWImhyoWj46BWJWvO5oucZJzRnVVhYtes2D9g+5MKXjSdR7Es3456hB89v4/1pcBKg==
react@^17.0.2:
version "17.0.2"