telegramBotStarterindex.ts1 match
30// This is a no-op if nothing's changed
31if (!isEndpointSet) {
32await bot.api.setWebhook(req.url, {
33secret_token: SECRET_TOKEN,
34});
x_posts_collect_mainmain.tsx59 matches
10const [query, setQuery] = React.useState("");
11const [targetAccount, setTargetAccount] = React.useState("");
12const [apiKey, setApiKey] = React.useState("");
13const [searchType, setSearchType] = React.useState("Latest");
14const [sinceDate, setSinceDate] = React.useState("");
39const checkAuth = async () => {
40try {
41const response = await fetch("/api/me");
42if (response.ok) {
43const userData = await response.json();
97setAuthError(null);
98try {
99const response = await fetch("/api/login", {
100method: "POST",
101headers: { "Content-Type": "application/json" },
120setAuthError(null);
121try {
122const response = await fetch("/api/register", {
123method: "POST",
124headers: { "Content-Type": "application/json" },
138const handleLogout = async () => {
139try {
140await fetch("/api/logout");
141setUser(null);
142// Clear sensitive data on logout
143setApiKey("");
144setTweets([]);
145setError(null);
154// No need to check for user here, useEffect handles it
155try {
156const response = await fetch("/api/searches");
157if (response.ok) {
158const searches = await response.json();
186if (!searchName) return;
187188const response = await fetch("/api/searches/save", {
189method: "POST",
190headers: { "Content-Type": "application/json" },
214if (!confirm("Are you sure you want to delete this saved search?")) return;
215try {
216const response = await fetch(`/api/searches/${searchId}/delete`, {
217method: "POST", // Using POST due to potential Val Town limitations
218});
242search.until_date,
243);
244// Ensure API key is present before running saved search
245if (!apiKey.trim()) {
246setError(
247"Please enter your SocialData API Key before running a saved search.",
248);
249return;
261return;
262}
263if (!apiKey.trim()) {
264setError("Please enter your SocialData API Key.");
265return;
266}
289params.append("cursor", currentCursor);
290}
291// *** Pass API key in header ***
292const response = await fetch(`/api/search?${params.toString()}`, {
293headers: { "X-Api-Key": apiKey }, // Pass API key here
294});
295306const errorData = await response
307.json()
308.catch(() => ({ message: `API Error: ${response.statusText}` }));
309throw new Error(
310errorData.message
311|| `API Error: ${response.statusText} on page ${pageNum}`,
312);
313}
318if (data.status === "error") {
319throw new Error(
320data.message || `API returned an error on page ${pageNum}`,
321);
322}
353}
354},
355[apiKey, user], // Add user dependency
356);
357367return;
368}
369if (!apiKey.trim()) {
370setError("Please enter your SocialData API Key.");
371return;
372}
562rel: "noopener noreferrer",
563},
564"SocialData API",
565),
566" | ",
616),
617),
618// --- API Key Input ---
619React.createElement(
620"div",
625React.createElement(
626"label",
627{ htmlFor: "apiKey", className: "label" },
628"API Key",
629),
630React.createElement("input", {
631type: "password",
632id: "apiKey",
633value: apiKey,
634onChange: (e) => setApiKey(e.target.value),
635placeholder: "Enter SocialData API Key",
636className: "input",
637}),
1088rel: "noopener noreferrer",
1089},
1090"SocialData API",
1091),
1092" | ",
1194}
11951196// --- API Endpoints ---
11971198// Register Endpoint
1199if (url.pathname === "/api/register" && request.method === "POST") {
1200try {
1201const { username, email, password } = await request.json();
12471248// Login Endpoint
1249if (url.pathname === "/api/login" && request.method === "POST") {
1250try {
1251const { email, password } = await request.json();
12901291// Get Current User Endpoint
1292if (url.pathname === "/api/me" && request.method === "GET") {
1293try {
1294const user = await getUserFromRequest(request);
13251326// Logout Endpoint
1327if (url.pathname === "/api/logout" && request.method === "GET") {
1328// Changed to GET for simplicity, POST is often preferred
1329return Response.json(
13411342// Save Search Endpoint
1343if (url.pathname === "/api/searches/save" && request.method === "POST") {
1344if (!user)
1345return Response.json({ error: "Not authenticated" }, { status: 401 });
13951396// Get Saved Searches Endpoint
1397if (url.pathname === "/api/searches" && request.method === "GET") {
1398if (!user)
1399return Response.json({ error: "Not authenticated" }, { status: 401 });
1415// Delete Saved Search Endpoint
1416if (
1417url.pathname.startsWith("/api/searches/")
1418&& url.pathname.endsWith("/delete")
1419&& (request.method === "POST" || request.method === "DELETE")
1447}
14481449// Search API Proxy Endpoint
1450if (url.pathname === "/api/search" && request.method === "GET") {
1451// *** LOGIN CHECK ADDED for the proxy endpoint ***
1452if (!user) {
1457}
14581459const apiKey = request.headers.get("X-Api-Key"); // Get API key from header sent by frontend
1460if (!apiKey) {
1461return Response.json(
1462{ status: "error", message: "API Key missing in request" },
1463{ status: 400 },
1464); // Changed status to 400
1476}
14771478const apiUrl = new URL("https://api.socialdata.tools/twitter/search");
1479apiUrl.searchParams.set("query", query);
1480apiUrl.searchParams.set("type", type);
1481if (cursor) apiUrl.searchParams.set("cursor", cursor);
14821483try {
1484const apiResponse = await fetch(apiUrl.toString(), {
1485headers: {
1486Authorization: `Bearer ${apiKey}`, // Use the API key from the request header
1487Accept: "application/json",
1488},
1491let responseData;
1492try {
1493responseData = await apiResponse.json();
1494} catch (e) {
1495const text = await apiResponse.text();
1496console.error("Non-JSON response from SocialData:", text);
1497return Response.json(
1498{
1499status: "error",
1500message: `SocialData API non-JSON response: ${apiResponse.statusText}`,
1501},
1502{ status: apiResponse.status },
1503);
1504}
15051506if (!apiResponse.ok || responseData.status === "error") {
1507console.error(
1508`SocialData API Error (${apiResponse.status}):`,
1509responseData,
1510);
1514status: "error",
1515message: responseData?.message
1516|| `SocialData API Error: ${apiResponse.statusText}`,
1517},
1518{ status: apiResponse.status },
1519);
1520}
9const [query, setQuery] = React.useState("");
10const [targetAccount, setTargetAccount] = React.useState("");
11const [apiKey, setApiKey] = React.useState("");
12const [searchType, setSearchType] = React.useState("Latest");
13const [sinceDate, setSinceDate] = React.useState("");
35const checkAuth = async () => {
36try {
37const response = await fetch("/api/me"); // サーバーに現在のユーザー情報を問い合わせる
38if (response.ok) {
39const userData = await response.json(); // 応答がOKならユーザーデータを取得
57if (!user) return; // ユーザーがログインしていない場合は何もしない(※useEffectの後に移動したのでこのチェックは不要かも)
58try {
59const response = await fetch("/api/searches"); // 保存済み検索APIを叩く
60if (response.ok) {
61const searches = await response.json();
9495try {
96const response = await fetch("/api/login", {
97// ログインAPIを叩く
98method: "POST",
99headers: { "Content-Type": "application/json" },
125126try {
127const response = await fetch("/api/register", {
128// 登録APIを叩く
129method: "POST",
130headers: { "Content-Type": "application/json" },
148const handleLogout = async () => {
149try {
150await fetch("/api/logout"); // ログアウトAPIを叩く
151setUser(null); // ユーザー情報をnullにする
152// setSavedSearches([]); // useEffectでuserが変わると自動でクリアされるので不要
178if (!searchName) return; // キャンセルされたら何もしない
179180const response = await fetch("/api/searches/save", {
181// 保存APIを叩く
182method: "POST",
183headers: { "Content-Type": "application/json" },
212213try {
214const response = await fetch(`/api/searches/${searchId}/delete`, {
215method: "POST", // Val Townの制限でDELETEが使えない場合があるのでPOSTを使う
216});
279const fetchAllTweets = React.useCallback(
280async (fullQuery, type, targetCount) => {
281if (!apiKey.trim()) {
282setError("Please enter your SocialData API Key.");
283return;
284}
306params.append("cursor", currentCursor);
307}
308const response = await fetch(`/api/search?${params.toString()}`, {
309headers: { "X-Api-Key": apiKey },
310});
311if (!isFetchingRef.current) break;
313const errorData = await response
314.json()
315.catch(() => ({ message: `API Error: ${response.statusText}` }));
316throw new Error(
317errorData.message ||
318`API Error: ${response.statusText} on page ${pageNum}`
319);
320}
323if (data.status === "error") {
324throw new Error(
325data.message || `API returned an error on page ${pageNum}`
326);
327}
356}
357},
358[apiKey]
359);
360365return;
366}
367if (!apiKey.trim()) {
368setError("Please enter your SocialData API Key.");
369return;
370}
596)
597),
598// API Key Section
599React.createElement(
600"div",
605React.createElement(
606"label",
607{ htmlFor: "apiKey", className: "label" },
608"API Key"
609),
610React.createElement("input", {
611type: "password",
612id: "apiKey",
613value: apiKey,
614onChange: (e) => setApiKey(e.target.value),
615placeholder: "Enter SocialData API Key",
616className: "input",
617})
1081rel: "noopener noreferrer",
1082},
1083"SocialData API"
1084),
1085" | ",
12011202// テーブル確認用エンドポイント
1203if (url.pathname === "/api/check-table") {
1204try {
1205const result = await sqlite.execute(`
12221223// ヘルパー関数テスト用エンドポイント(単純化)
1224if (url.pathname === "/api/test-helpers") {
1225try {
1226// パスワードハッシュ化のテスト
12471248// ユーザー登録エンドポイント
1249if (url.pathname === "/api/register") {
1250if (request.method !== "POST") {
1251return Response.json({ error: "Method not allowed" }, { status: 405 });
13381339// ユーザーログインエンドポイント
1340if (url.pathname === "/api/login") {
1341if (request.method !== "POST") {
1342return Response.json({ error: "Method not allowed" }, { status: 405 });
14091410// 現在のユーザー情報取得エンドポイント
1411if (url.pathname === "/api/me") {
1412try {
1413// ユーザー情報を取得
14461447// ログアウトエンドポイント
1448if (url.pathname === "/api/logout") {
1449return Response.json(
1450{ success: true },
14601461// 検索条件保存エンドポイント
1462if (url.pathname === "/api/searches/save") {
1463if (request.method !== "POST") {
1464return Response.json({ error: "Method not allowed" }, { status: 405 });
15451546// 保存済み検索一覧取得エンドポイント
1547if (url.pathname === "/api/searches") {
1548try {
1549// ユーザー情報を取得
1575// 保存済み検索削除エンドポイント
1576if (
1577url.pathname.startsWith("/api/searches/") &&
1578url.pathname.includes("/delete")
1579) {
1625}
16261627// 既存のAPI検索エンドポイント
1628if (url.pathname === "/api/search") {
1629const apiKey = request.headers.get("X-Api-Key");
1630if (!apiKey) {
1631return Response.json(
1632{ status: "error", message: "API Key missing" },
1633{ status: 401 }
1634);
1643);
1644}
1645const apiUrl = new URL("https://api.socialdata.tools/twitter/search");
1646apiUrl.searchParams.set("query", query);
1647apiUrl.searchParams.set("type", type);
1648if (cursor) apiUrl.searchParams.set("cursor", cursor);
16491650try {
1651const apiResponse = await fetch(apiUrl.toString(), {
1652headers: {
1653Authorization: `Bearer ${apiKey}`,
1654Accept: "application/json",
1655},
1657let responseData;
1658try {
1659responseData = await apiResponse.json();
1660} catch (e) {
1661const text = await apiResponse.text();
1662console.error("Non-JSON response:", text);
1663return Response.json(
1664{
1665status: "error",
1666message: `API non-JSON response: ${apiResponse.statusText}`,
1667},
1668{
1669status: apiResponse.status,
1670}
1671);
1672}
1673if (!apiResponse.ok || responseData.status === "error") {
1674console.error(`API Error (${apiResponse.status}):`, responseData);
1675return Response.json(
1676{
1677status: "error",
1678message:
1679responseData?.message || `API Error: ${apiResponse.statusText}`,
1680},
1681{ status: apiResponse.status }
1682);
1683}
stevensDemosendDailyBrief.ts8 matches
9798export async function sendDailyBriefing(chatId?: string, today?: DateTime) {
99// Get API keys from environment
100const apiKey = Deno.env.get("ANTHROPIC_API_KEY");
101const telegramToken = Deno.env.get("TELEGRAM_TOKEN");
102106}
107108if (!apiKey) {
109console.error("Anthropic API key is not configured.");
110return;
111}
122123// Initialize Anthropic client
124const anthropic = new Anthropic({ apiKey });
125126// Initialize Telegram bot
162163// disabled title for now, it seemes unnecessary...
164// await bot.api.sendMessage(chatId, `*${title}*`, { parse_mode: "Markdown" });
165166// Then send the main content
169170if (content.length <= MAX_LENGTH) {
171await bot.api.sendMessage(chatId, content, { parse_mode: "Markdown" });
172// Store the briefing in chat history
173await storeChatMessage(
198// Send each chunk as a separate message and store in chat history
199for (const chunk of chunks) {
200await bot.api.sendMessage(chatId, chunk, { parse_mode: "Markdown" });
201// Store each chunk in chat history
202await storeChatMessage(
stevensDemoREADME.md1 match
53You'll need to set up some environment variables to make it run.
5455- `ANTHROPIC_API_KEY` for LLM calls
56- You'll need to follow [these instructions](https://docs.val.town/integrations/telegram/) to make a telegram bot, and set `TELEGRAM_TOKEN`. You'll also need to get a `TELEGRAM_CHAT_ID` in order to have the bot remember chat contents.
57- For the Google Calendar integration you'll need `GOOGLE_CALENDAR_ACCOUNT_ID` and `GOOGLE_CALENDAR_CALENDAR_ID`. See [these instuctions](https://www.val.town/v/stevekrouse/pipedream) for details.
stevensDemoREADME.md5 matches
8## Hono
910This app uses [Hono](https://hono.dev/) as the API framework. You can think of Hono as a replacement for [ExpressJS](https://expressjs.com/) that works in serverless environments like Val Town or Cloudflare Workers. If you come from Python or Ruby, Hono is also a lot like [Flask](https://github.com/pallets/flask) or [Sinatra](https://github.com/sinatra/sinatra), respectively.
1112## Serving assets to the frontend
20### `index.html`
2122The most complicated part of this backend API is serving index.html. In this app (like most apps) we serve it at the root, ie `GET /`.
2324We *bootstrap* `index.html` with some initial data from the server, so that it gets dynamically injected JSON data without having to make another round-trip request to the server to get that data on the frontend. This is a common pattern for client-side rendered apps.
2526## CRUD API Routes
2728This app has two CRUD API routes: for reading and inserting into the messages table. They both speak JSON, which is standard. They import their functions from `/backend/database/queries.ts`. These routes are called from the React app to refresh and update data.
2930## Errors
3132Hono and other API frameworks have a habit of swallowing up Errors. We turn off this default behavior by re-throwing errors, because we think most of the time you'll want to see the full stack trace instead of merely "Internal Server Error". You can customize how you want errors to appear.
stevensDemoNotebookView.tsx5 matches
8import { type Memory } from "../../shared/types.ts";
910const API_BASE = "/api/memories";
11const MEMORIES_PER_PAGE = 20;
1271setError(null);
72try {
73const response = await fetch(API_BASE);
74if (!response.ok) {
75throw new Error(`HTTP error! status: ${response.status}`);
100101try {
102const response = await fetch(API_BASE, {
103method: "POST",
104headers: { "Content-Type": "application/json" },
123124try {
125const response = await fetch(`${API_BASE}/${id}`, {
126method: "DELETE",
127});
155156try {
157const response = await fetch(`${API_BASE}/${editingMemory.id}`, {
158method: "PUT",
159headers: { "Content-Type": "application/json" },
stevensDemoindex.ts11 matches
26});
2728// --- API Routes for Memories ---
2930// GET /api/memories - Retrieve all memories
31app.get("/api/memories", async (c) => {
32const memories = await getAllMemories();
33return c.json(memories);
34});
3536// POST /api/memories - Create a new memory
37app.post("/api/memories", async (c) => {
38const body = await c.req.json<Omit<Memory, "id">>();
39if (!body.text) {
44});
4546// PUT /api/memories/:id - Update an existing memory
47app.put("/api/memories/:id", async (c) => {
48const id = c.req.param("id");
49const body = await c.req.json<Partial<Omit<Memory, "id">>>();
66});
6768// DELETE /api/memories/:id - Delete a memory
69app.delete("/api/memories/:id", async (c) => {
70const id = c.req.param("id");
71try {
83// --- Blob Image Serving Routes ---
8485// GET /api/images/:filename - Serve images from blob storage
86app.get("/api/images/:filename", async (c) => {
87const filename = c.req.param("filename");
88
stevensDemoindex.html2 matches
12type="image/svg+xml"
13/>
14<link rel="preconnect" href="https://fonts.googleapis.com" />
15<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
16<link
17href="https://fonts.googleapis.com/css2?family=Pixelify+Sans:wght@400..700&display=swap"
18rel="stylesheet"
19/>