Add weather caching, useless-tip redirect, clock widget, and IAM Bedrock auth
- Cache the latest fetched tomorrow weather and expose it via /api/system/weather/latest - Add a header Clock widget showing date/time plus the cached weather summary - Redirect useless-talk mentions from the weather bot to the useless-tips webhook - Switch Bedrock auth from bearer token to standard IAM access key/secret Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
|
||||
interface LatestWeather {
|
||||
weather: string
|
||||
pop: number | null
|
||||
judgement: string
|
||||
reason: string
|
||||
fetched_at: string
|
||||
}
|
||||
|
||||
const now = ref(new Date())
|
||||
const weather = ref<LatestWeather | null>(null)
|
||||
let clockIntervalId: number | null = null
|
||||
let weatherIntervalId: number | null = null
|
||||
|
||||
const formatDate = (date: Date) => {
|
||||
const weekdays = ['日', '月', '火', '水', '木', '金', '土']
|
||||
return `${date.getFullYear()}/${String(date.getMonth() + 1).padStart(2, '0')}/${String(date.getDate()).padStart(2, '0')} (${weekdays[date.getDay()]})`
|
||||
}
|
||||
|
||||
const formatTime = (date: Date) => {
|
||||
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}:${String(date.getSeconds()).padStart(2, '0')}`
|
||||
}
|
||||
|
||||
const formatFetchedAt = (iso: string) => {
|
||||
const d = new Date(iso)
|
||||
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}時点`
|
||||
}
|
||||
|
||||
const fetchWeather = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/system/weather/latest')
|
||||
const data = await response.json()
|
||||
weather.value = data
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch latest weather:', error)
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
clockIntervalId = window.setInterval(() => {
|
||||
now.value = new Date()
|
||||
}, 1000)
|
||||
|
||||
fetchWeather()
|
||||
weatherIntervalId = window.setInterval(fetchWeather, 60000)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (clockIntervalId) clearInterval(clockIntervalId)
|
||||
if (weatherIntervalId) clearInterval(weatherIntervalId)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="clock">
|
||||
<span class="date">{{ formatDate(now) }}</span>
|
||||
<span class="time">{{ formatTime(now) }}</span>
|
||||
<span v-if="weather" class="weather" :title="`${formatFetchedAt(weather.fetched_at)}取得`">
|
||||
🌤️ 明日: {{ weather.weather }}
|
||||
<template v-if="weather.pop !== null">(降水{{ weather.pop }}%)</template>
|
||||
・👕{{ weather.judgement }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.clock {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8px;
|
||||
font-family: monospace;
|
||||
font-size: 13px;
|
||||
color: #aaa;
|
||||
}
|
||||
.time {
|
||||
color: #eee;
|
||||
font-weight: 600;
|
||||
}
|
||||
.weather {
|
||||
color: #8bc8ff;
|
||||
font-size: 12px;
|
||||
border-left: 1px solid #444;
|
||||
padding-left: 8px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user