chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:44:08 +08:00
commit 983960e2dd
1244 changed files with 281996 additions and 0 deletions
@@ -0,0 +1,10 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { deleteAllTimersMemory } from '../lib/memory'
export const run: ActionFunction = async function () {
await deleteAllTimersMemory()
await leon.answer({ key: 'timer_canceled' })
}
@@ -0,0 +1,45 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { TimerWidget } from '../widgets/timer-widget'
import { getTimerMemoryByWidgetId, getNewestTimerMemory } from '../lib/memory'
export const run: ActionFunction = async function (_params, paramsHelper) {
const widgetId = paramsHelper.getWidgetId()
const timerMemory = widgetId
? await getTimerMemoryByWidgetId(widgetId)
: await getNewestTimerMemory()
if (!timerMemory) {
await leon.answer({ key: 'no_timer_set' })
return
}
const { interval, finishedAt, duration } = timerMemory
let remainingTime = finishedAt - Math.floor(Date.now() / 1_000)
if (remainingTime <= 0) {
remainingTime = 0
}
const initialProgress = 100 - (remainingTime / duration) * 100
const timerWidget = new TimerWidget({
params: {
seconds: remainingTime,
initialProgress,
initialDuration: duration,
interval
},
onFetch: {
widgetId: widgetId ?? timerMemory.widgetId,
actionName: 'check_timer'
}
})
await leon.answer({
widget: timerWidget,
key: 'timer_status',
data: {
remaining_time: remainingTime
}
})
}
@@ -0,0 +1,57 @@
import type { ActionFunction } from '@sdk/types'
import { leon } from '@sdk/leon'
import { TimerWidget } from '../widgets/timer-widget'
import { createTimerMemory } from '../lib/memory'
interface TimerDuration {
value?: number
unit?: string
}
export const run: ActionFunction = async function (params) {
const supportedUnits = ['hours', 'minutes', 'seconds']
const duration = (params.action_arguments['duration'] as TimerDuration) || null
if (
!duration ||
typeof duration.value !== 'number' ||
typeof duration.unit !== 'string'
) {
await leon.answer({ key: 'cannot_get_duration' })
return
}
const normalizedUnit = duration.unit.toLowerCase()
if (!supportedUnits.includes(normalizedUnit)) {
await leon.answer({ key: 'unit_not_supported' })
return
}
const { value: durationValue } = duration
const seconds =
normalizedUnit === 'hours'
? Number(durationValue) * 3_600
: normalizedUnit === 'minutes'
? Number(durationValue) * 60
: Number(durationValue)
const interval = 1_000
const timerWidget = new TimerWidget({
params: {
seconds,
initialProgress: 0,
interval
},
onFetch: {
actionName: 'check_timer'
}
})
await Promise.all([
createTimerMemory(timerWidget.id, seconds, interval),
leon.answer({
widget: timerWidget,
key: 'timer_set'
})
])
}
@@ -0,0 +1,55 @@
import { Memory } from '@sdk/memory'
export interface TimerMemory {
widgetId: string
duration: number
interval: number
createdAt: number
finishedAt: number
}
const TIMERS_MEMORY = new Memory<TimerMemory[]>({
name: 'timers',
defaultMemory: []
})
export async function createTimerMemory(
widgetId: string,
duration: number,
interval: number
): Promise<TimerMemory> {
const createdAt = Math.floor(Date.now() / 1_000)
const newTimerMemory: TimerMemory = {
duration,
widgetId,
interval,
createdAt,
finishedAt: createdAt + duration
}
const timersMemory = await TIMERS_MEMORY.read()
await TIMERS_MEMORY.write([...timersMemory, newTimerMemory])
return newTimerMemory
}
export async function getTimerMemoryByWidgetId(
widgetId: string
): Promise<TimerMemory | null> {
const timersMemory = await TIMERS_MEMORY.read()
return (
timersMemory.find((timerMemory) => timerMemory.widgetId === widgetId) ||
null
)
}
export async function getNewestTimerMemory(): Promise<TimerMemory | null> {
const timersMemory = await TIMERS_MEMORY.read()
return timersMemory[timersMemory.length - 1] || null
}
export function deleteAllTimersMemory(): Promise<TimerMemory[]> {
return TIMERS_MEMORY.write([])
}
@@ -0,0 +1 @@
{}
@@ -0,0 +1,16 @@
import type { WidgetEventMethod } from '@sdk/widget'
import { WidgetComponent } from '@sdk/widget-component'
interface TimerProps {
initialTime: number
initialProgress: number
interval: number
totalTimeContent: string
onEnd?: () => WidgetEventMethod
}
export class Timer extends WidgetComponent<TimerProps> {
constructor(props: TimerProps) {
super(props)
}
}
@@ -0,0 +1,53 @@
import type { WidgetComponent } from '@sdk/widget-component'
import { Widget, type WidgetEventMethod, type WidgetOptions } from '@sdk/widget'
import { Timer } from './components/timer'
interface Params {
seconds: number
interval: number
initialProgress: number
initialDuration?: number
}
export class TimerWidget extends Widget<Params> {
constructor(options: WidgetOptions<Params>) {
super(options)
}
public render(): WidgetComponent {
const { seconds, interval, initialDuration, initialProgress } = this.params
const secondUnitContent = this.content('second_unit')
const secondsUnitContent = this.content('seconds_unit')
const minuteUnitContent = this.content('minute_unit')
const minutesUnitContent = this.content('minutes_unit')
const totalTime = initialDuration || seconds
let totalTimeContent = ''
if (totalTime >= 60) {
const minutes = totalTime / 60
totalTimeContent = this.content('total_time', {
value: minutes % 1 === 0 ? minutes : minutes.toFixed(2),
unit: minutes > 1 ? minutesUnitContent : minuteUnitContent
})
} else {
totalTimeContent = this.content('total_time', {
value: totalTime,
unit: totalTime > 1 ? secondsUnitContent : secondUnitContent
})
}
return new Timer({
initialTime: seconds,
initialProgress,
interval,
totalTimeContent,
onEnd: (): WidgetEventMethod => {
return this.sendUtterance('times_up', {
from: 'leon'
})
}
})
}
}