diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionProfile.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionProfile.tsx index 6624d439..1f55ef62 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionProfile.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionProfile.tsx @@ -13,6 +13,7 @@ import { X } from 'lucide-react'; import { Avatar } from '@/components/common/Avatar'; import { LoadingPanel } from '@/components/common/LoadingPanel'; import { useMessages, useWebsiteSessionQuery } from '@/components/hooks'; +import { isLikelyBot } from '@/lib/botDetection'; import { SessionActivity } from './SessionActivity'; import { SessionData } from './SessionData'; import { SessionInfo } from './SessionInfo'; @@ -51,7 +52,7 @@ export function SessionProfile({ )} - + diff --git a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx index 5d3bb374..60632fed 100644 --- a/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx +++ b/src/app/(main)/websites/[websiteId]/sessions/SessionsTable.tsx @@ -4,6 +4,7 @@ import { Avatar } from '@/components/common/Avatar'; import { DateDistance } from '@/components/common/DateDistance'; import { TypeIcon } from '@/components/common/TypeIcon'; import { useFormat, useMessages, useNavigation } from '@/components/hooks'; +import { isLikelyBot } from '@/lib/botDetection'; export function SessionsTable(props: DataTableProps) { const { formatMessage, labels } = useMessages(); @@ -15,7 +16,7 @@ export function SessionsTable(props: DataTableProps) { {(row: any) => ( - + )} diff --git a/src/components/common/Avatar.tsx b/src/components/common/Avatar.tsx index 9b198b30..3277ffca 100644 --- a/src/components/common/Avatar.tsx +++ b/src/components/common/Avatar.tsx @@ -1,21 +1,27 @@ -import { lorelei } from '@dicebear/collection'; -import { createAvatar } from '@dicebear/core'; +import { bottts, lorelei } from '@dicebear/collection'; +import { createAvatar, type Style } from '@dicebear/core'; import { useMemo } from 'react'; import { getColor, getPastel } from '@/lib/colors'; -const lib = lorelei; - -export function Avatar({ seed, size = 128, ...props }: { seed: string; size?: number }) { +export function Avatar({ + seed, + size = 128, + isBot = false, +}: { + seed: string; + size?: number; + isBot?: boolean; +}) { const backgroundColor = getPastel(getColor(seed), 4); + const style = (isBot ? bottts : lorelei) as Style; const avatar = useMemo(() => { - return createAvatar(lib, { - ...props, + return createAvatar(style, { seed, size, backgroundColor: [backgroundColor], }).toDataUri(); - }, []); + }, [seed, isBot]); return Avatar; } diff --git a/src/lib/botDetection.ts b/src/lib/botDetection.ts new file mode 100644 index 00000000..b3a85a8c --- /dev/null +++ b/src/lib/botDetection.ts @@ -0,0 +1,25 @@ +const BOT_CITIES = [ + 'Council Bluffs', + 'North Richland Hills', + 'Santa Clara', + 'Ashburn', + 'The Dalles', + 'Boardman', + 'Quincy', +]; + +export function isLikelyBot(session: { + city?: string; + firstAt?: string | Date; + lastAt?: string | Date; +}): boolean { + const cityMatch = + session.city && BOT_CITIES.some(botCity => session.city?.toLowerCase() === botCity.toLowerCase()); + + const zeroDuration = + session.firstAt && + session.lastAt && + new Date(session.firstAt).getTime() === new Date(session.lastAt).getTime(); + + return !!(cityMatch && zeroDuration); +}