7import { sqlite } from "https://esm.town/v/std/sqlite";
89// Discord API endpoints
10const API_BASE = "https://discord.com/api/v10";
1112// Initialize the Discord rate limit service
47}
4849// Enhanced Discord API request with rate limiting
50async function discordRequest(endpoint: string, options: RequestInit = {}) {
51const token = Deno.env.get("DISCORD_BOT_TOKEN");
54}
5556const url = `${API_BASE}${endpoint}`;
57const headers = {
58"Authorization": `Bot ${token}`,
84}
8586console.error(`❌ Discord API error: ${response.status}`, errorBody);
87throw new Error(`Discord API error: ${response.status} ${JSON.stringify(errorBody)}`);
88}
89189const routeKey = `/channels/${channelId}/messages`;
190191// Discord API has a limit of 100 messages per request, so we need to paginate
192while (hasMore) {
193const endpoint = lastId
204lastId = messages[messages.length - 1].id;
205206// If we've gotten more than 1000 messages, stop to avoid excessive API calls
207if (allMessages.length >= 1000) {
208console.log("Reached 1000 message limit, stopping pagination");
236}
237238// Get user info from Discord API with rate limiting
239async function getUserInfo(guildId: string, userId: string) {
240try {
16}
1718const url = `https://discord.com/api/v10${endpoint}`;
19const headers = {
20"Authorization": `Bot ${token}`,
3435if (!response.ok) {
36console.error(`❌ Discord API error: ${response.status}`);
37throw new Error(`Discord API error: ${response.status}`);
38}
39
1// discord-rate-limit-service.ts - Advanced rate limiting for Discord API with
2// exponential backoff, circuit breaker, global rate limit handling, and blob persistence
314}
1516// API call tracking for global rate limits
17interface ApiCallRecord {
18timestamp: number;
19}
33private static instance: DiscordRateLimitService;
34private rateLimitBuckets: Map<string, RateLimitInfo> = new Map();
35private apiCallTimes: ApiCallRecord[] = [];
36private globalLock: boolean = false;
37217console.log(`⚡ Circuit breaker open, blocking requests for ${waitTime} more seconds`);
218throw new Error(
219`Circuit breaker open. API requests blocked for ${waitTime} more seconds due to too many failures.`,
220);
221}
263}
264265// Main method to execute API calls with rate limiting
266public async executeWithRateLimit<T>(
267routeKey: string,
289await this.ensureDynamicRateLimitCompliance(routeKey);
290291// Execute the actual API call
292const result = await action();
293401// Check for Discord's global rate limit message
402if (error.message && typeof error.message === "string") {
403if (error.message.includes("You are being blocked from accessing our API temporarily")) {
404// Set a longer global rate limit time (e.g., 10 minutes)
405this.globalRateLimitUntil = Date.now() + 10 * 60 * 1000;
567568// Also track for global rate limiting
569this.trackApiCall();
570}
571572// Track an API call for global rate limiting
573private trackApiCall(): void {
574const now = Date.now();
575this.apiCallTimes.push({ timestamp: now });
576577// Clean up old records
578this.apiCallTimes = this.apiCallTimes.filter(call => now - call.timestamp <= this.RATE_LIMIT_WINDOW_MS);
579}
580651652// Clean up old records
653this.apiCallTimes = this.apiCallTimes.filter(call => now - call.timestamp <= this.RATE_LIMIT_WINDOW_MS);
654655// If we have capacity, return immediately
656if (this.apiCallTimes.length < this.RATE_LIMIT_PER_SECOND) {
657return;
658}
659660// Calculate how long to wait before making another request
661if (this.apiCallTimes.length > 0) {
662// Get the oldest API call in our window
663const oldestCall = this.apiCallTimes[0];
664665// Calculate when that call will expire from our window
luciaMagicLinkStarterindex.ts2 matches
15app.get("/frontend/**/*", c => serveFile(c.req.path, import.meta.url));
1617// Add your API routes here
18// app.get("/api/data", c => c.json({ hello: "world" }));
1920// Unwrap and rethrow Hono errors as the original error
19import type {
20HellLetLooseMapData,
21MapInfo,
22} from "https://esm.town/v/ktodaz/Discord_Bot_Services/map_vote/map-vote-getHellLetLooseMapData.tsx";
2324import type { CurrentConfig } from "https://esm.town/v/ktodaz/Discord_Bot_Services/map_vote/map-vote-getCurrentConfig.tsx";
2526// Discord API endpoints
27const API_BASE = "https://discord.com/api/v10";
2829// Initialize the Discord rate limit service
44};
4546// Enhanced Discord API request function with rate limiting
47async function discordRequest(endpoint: string, options: RequestInit = {}) {
48const token = Deno.env.get("DISCORD_BOT_TOKEN");
51}
5253const url = `${API_BASE}${endpoint}`;
54const headers = {
55"Authorization": `Bot ${token}`,
82}
8384console.error(`❌ Discord API error: ${response.status}`, errorBody);
85throw new Error(`Discord API error: ${response.status} ${JSON.stringify(errorBody)}`);
86}
87106107return rateLimitService.executeWithRateLimit(routeKey, async () => {
108const url = `${API_BASE}/channels/${channelId}/messages/${messageId}/reactions/${emoji}/@me`;
109const token = Deno.env.get("DISCORD_BOT_TOKEN");
110237title: initialEmbedData.InitialEmbedTitle || "Loading . . .",
238description: initialEmbedData.InitialEmbedDescription
239|| "Due to Discord API rate limits this will take a few minutes.",
240fields: [
241{
272273// Process variants for a map and add emoji reactions
274async function processMapVariants(messageId: string, channelId: string, map: MapInfo, variantOptions: any) {
275const mapMetaVariants: string[] = [];
276const mapEnabledVariants: string[] = [];
crypto-geminiscript.tsx18 matches
1// Val Town Backend: cryptoDataEndpoint.tsx
2// Make sure to set the COINGECKO_API_KEY environment variable in Val Town
34import { fetch } from "npm:undici"; // Use undici for fetch in Node.js environment
50}
5152const COINGECKO_API_KEY = Deno.env.get("COINGECKO_API_KEY");
53const COINGECKO_API_BASE = COINGECKO_API_KEY
54? "https://pro-api.coingecko.com/api/v3"
55: "https://api.coingecko.com/api/v3";
5657const FNG_API_URL = "https://api.alternative.me/fng/?limit=1";
5859async function fetchFromApi<T>(url: string, isCoinGecko: boolean = true): Promise<T | null> {
60const headers: HeadersInit = {};
61if (isCoinGecko && COINGECKO_API_KEY) {
62headers["x-cg-pro-api-key"] = COINGECKO_API_KEY;
63// or "x-cg-demo-api-key" if that's the header for your key
64}
6568if (!response.ok) {
69const errorText = await response.text();
70console.error(`API Error for ${url}: ${response.status} ${response.statusText}`, errorText);
71return null;
72}
100topCoinsData,
101] = await Promise.all([
102fetchFromApi<CoinGeckoMarketCoin[]>(`${COINGECKO_API_BASE}/coins/markets?vs_currency=usd&ids=bitcoin`),
103fetchFromApi<CoinGeckoChartData>(
104`${COINGECKO_API_BASE}/coins/bitcoin/market_chart?vs_currency=usd&days=30&interval=daily`,
105),
106fetchFromApi<FearAndGreedData>(FNG_API_URL, false), // false for isCoinGecko
107fetchFromApi<CoinGeckoGlobalData>(`${COINGECKO_API_BASE}/global`),
108fetchFromApi<CoinGeckoMarketCoin[]>(
109`${COINGECKO_API_BASE}/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=10&page=1&sparkline=true&price_change_percentage=7d`,
110),
111]);
1export interface MapInfo {
2MapName: string;
3MapDetails: MapDetails;
2021export interface HellLetLooseMapData {
22Maps: MapInfo[];
23}
24
15return {
16InitialEmbedTitle: "Loading . . .",
17InitialEmbedDescription: "Due to Discord API rate limits this will take a few minutes.",
18InitialEmbedWarningFieldTitle: "⚠️ Warning ⚠️",
19InitialEmbedWarningFieldValue: "Do not send messages while channel is loading!",
47console.log("lol");
4849await fetch(`https://mastodon.social/api/v1/statuses`, {
50method: "POST",
51headers: {
hn-remote-ts-genai-jobsapi.ts9 matches
1/**
2* HTTP API for HN Remote TypeScript + GenAI Jobs
3* Provides endpoints to view jobs and trigger refreshes
4*/
17if (path === "/" || path === "/jobs") {
18return handleJobsPage(req);
19} else if (path === "/api/jobs") {
20return handleJobsAPI(req);
21} else if (path === "/api/refresh" && req.method === "POST") {
22return handleRefresh(req);
23} else {
45<h1>Remote TypeScript + GenAI Jobs</h1>
46<p>No job data available yet. Please run the job finder first.</p>
47<form action="/api/refresh" method="post">
48<button type="submit" style="padding: 8px 16px; background: #0366d6; color: white; border: none; border-radius: 4px; cursor: pointer;">
49Refresh Data
73<div style="display: flex; justify-content: space-between; align-items: center;">
74<h1>Remote TypeScript + GenAI Jobs</h1>
75<form action="/api/refresh" method="post">
76<button type="submit" style="padding: 8px 16px; background: #0366d6; color: white; border: none; border-radius: 4px; cursor: pointer;">
77Refresh Data
88<footer style="margin-top: 40px; text-align: center; color: #666;">
89<p>
90<a href="/api/jobs" target="_blank">JSON API</a> |
91<a href="${import.meta.url.replace("esm.town", "val.town")}" target="_blank">View Source</a>
92</p>
102103/**
104* Handle the JSON API endpoint
105*/
106async function handleJobsAPI(req: Request): Promise<Response> {
107// Get the format parameter
108const url = new URL(req.url);