@jkt48/core
Version:
Official JKT48 Connect API client for Node.js and browsers
682 lines (598 loc) • 17.6 kB
JavaScript
import axios from 'axios';
/**
* Constants used throughout the JKT48 API client
*/
const BASE_URL = 'https://v2.jkt48connect.com/api';
const ENDPOINTS = {
// Member endpoints
MEMBERS: '/jkt48/members',
BIRTHDAY: '/jkt48/birthday',
MEMBER_DETAIL: (name) => `/jkt48/member/${encodeURIComponent(name)}`,
// Event endpoints
EVENTS: '/jkt48/events',
THEATER: '/jkt48/theater',
THEATER_DETAIL: (id) => `/jkt48/theater/${id}`,
// Live streaming endpoints
RECENT: '/jkt48/recent',
REPLAY: '/jkt48/replay',
RECENT_DETAIL: (liveId) => `/jkt48/recent/${liveId}`,
LIVE: '/jkt48/live',
LIVE_YOUTUBE: '/jkt48/live/youtube',
LIVE_IDN: '/jkt48/live/idn',
LIVE_SHOWROOM: '/jkt48/live/showroom',
// Media endpoints
YOUTUBE: '/jkt48/youtube',
NEWS: '/jkt48/news',
NEWS_DETAIL: (id) => `/jkt48/news/${id}`,
// Chat endpoints
CHAT_STREAM: (username, slug) => `/jkt48/chat-stream?username=${encodeURIComponent(username)}&slug=${encodeURIComponent(slug)}`,
CHAT_STREAM_SR: (roomId) => `/jkt48/chat-stream-sr?room_id=${encodeURIComponent(roomId)}`,
// Video call endpoints
VIDEO_CALL: (sesi = '', date = '', member = '') => {
let endpoint = `/jkt48/videocall`;
const params = [];
if (sesi) params.push(`sesi=${encodeURIComponent(sesi)}`);
if (date) params.push(`date=${encodeURIComponent(date)}`);
if (member) params.push(`member=${encodeURIComponent(member)}`);
return params.length > 0 ? `${endpoint}?${params.join('&')}` : endpoint;
},
VIDEO_CALL_TODAY: '/jkt48/videocall/today',
// System endpoints
CHECK: '/zenova/check',
// Admin endpoints
ADMIN_KEYS: '/admin/keys',
ADMIN_KEY_DETAIL: (key) => `/admin/key/${encodeURIComponent(key)}`,
ADMIN_UPDATE_KEY: (key, active, type) => `/admin/update-key?key=${encodeURIComponent(key)}&active=${active}&type=${encodeURIComponent(type)}`,
ADMIN_DELETE_KEY: (key) => `/admin/key/${encodeURIComponent(key)}`,
ADMIN_STATS: '/admin/stats',
ADMIN_CREATE_KEY: (owner, email, type = '', apikey = '') => {
let endpoint = `/admin/create-key?owner=${encodeURIComponent(owner)}&email=${encodeURIComponent(email)}`;
if (type) endpoint += `&type=${encodeURIComponent(type)}`;
if (apikey) endpoint += `&apikey=${encodeURIComponent(apikey)}`;
return endpoint;
},
ADMIN_ADD_LIMIT: (key, additionalLimit) => `/admin/add-limit?key=${encodeURIComponent(key)}&additionalLimit=${encodeURIComponent(additionalLimit)}`,
ADMIN_ADD_EXPIRY: (key, additionalDays) => `/admin/add-expiry?key=${encodeURIComponent(key)}&additionalDays=${encodeURIComponent(additionalDays)}`,
// Changelog endpoints
CREATE_CHANGELOG: '/database/create-changelog',
CHANGELOGS: '/database/changelogs',
CHANGELOG_DETAIL: (id) => `/database/changelog/${id}`,
UPDATE_CHANGELOG: (id) => `/database/changelog/${id}`,
DELETE_CHANGELOG: (id) => `/database/changelog/${id}`,
// Grow A Garden endpoints
STOCK: '/growagarden/stock',
WEATHER: '/growagarden/weather',
RESTOCK: '/growagarden/restock-timer',
ALL: '/growagarden/all'
};
/**
* HTTP Client for making API requests
*/
class HttpClient {
constructor(apiKey, priorityToken = null, adminCredentials = null) {
this.apiKey = apiKey;
this.priorityToken = priorityToken;
this.adminCredentials = adminCredentials;
this.baseURL = BASE_URL;
}
/**
* Build request config with authentication
*/
buildConfig(config = {}) {
const headers = {
'Content-Type': 'application/json',
...config.headers
};
// Add priority token if available
if (this.priorityToken) {
headers['x-priority-token'] = this.priorityToken;
}
return {
...config,
headers
};
}
/**
* Build URL with query parameters
*/
buildUrl(endpoint, params = {}) {
const url = new URL(`${this.baseURL}${endpoint}`);
// Add API key
url.searchParams.append('apikey', this.apiKey);
// Add priority token as query param if available
if (this.priorityToken && !params.priority_token) {
url.searchParams.append('priority_token', this.priorityToken);
}
// Add admin credentials if available
if (this.adminCredentials) {
url.searchParams.append('username', this.adminCredentials.username);
url.searchParams.append('password', this.adminCredentials.password);
}
// Add other parameters
Object.keys(params).forEach(key => {
if (params[key] !== undefined && params[key] !== null) {
url.searchParams.append(key, params[key]);
}
});
return url.toString();
}
/**
* Make GET request
*/
async get(endpoint, params = {}, config = {}) {
try {
const url = this.buildUrl(endpoint, params);
const response = await axios.get(url, this.buildConfig(config));
return response.data;
} catch (error) {
this.handleError(error);
}
}
/**
* Make POST request
*/
async post(endpoint, data = {}, params = {}, config = {}) {
try {
const url = this.buildUrl(endpoint, params);
// Add priority token to body if available
if (this.priorityToken && !data.priority_token) {
data.priority_token = this.priorityToken;
}
const response = await axios.post(url, data, this.buildConfig(config));
return response.data;
} catch (error) {
this.handleError(error);
}
}
/**
* Make PUT request
*/
async put(endpoint, data = {}, params = {}, config = {}) {
try {
const url = this.buildUrl(endpoint, params);
// Add priority token to body if available
if (this.priorityToken && !data.priority_token) {
data.priority_token = this.priorityToken;
}
const response = await axios.put(url, data, this.buildConfig(config));
return response.data;
} catch (error) {
this.handleError(error);
}
}
/**
* Make DELETE request
*/
async delete(endpoint, params = {}, config = {}) {
try {
const url = this.buildUrl(endpoint, params);
const response = await axios.delete(url, this.buildConfig(config));
return response.data;
} catch (error) {
this.handleError(error);
}
}
/**
* Handle API errors
*/
handleError(error) {
if (error.response) {
// Server responded with error
const errorMessage = error.response.data?.message || error.response.statusText;
throw new Error(`API Error (${error.response.status}): ${errorMessage}`);
} else if (error.request) {
// Request made but no response
throw new Error('Network Error: No response from server');
} else {
// Something else happened
throw new Error(`Request Error: ${error.message}`);
}
}
}
/**
* Service for member-related API endpoints
*/
class MembersService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get all JKT48 members
* @returns {Promise<Object>} List of all members
*/
async getMembers() {
return this.client.get(ENDPOINTS.MEMBERS);
}
/**
* Get member details by name
* @param {string} name - Member name
* @returns {Promise<Object>} Member details
*/
async getMemberDetail(name) {
return this.client.get(ENDPOINTS.MEMBER_DETAIL(name));
}
/**
* Get members with birthdays
* @returns {Promise<Object>} Members birthday information
*/
async getBirthdays() {
return this.client.get(ENDPOINTS.BIRTHDAY);
}
}
/**
* Service for live streaming related API endpoints
*/
class LiveService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get current live streams
* @returns {Promise<Object>} Current live streams
*/
async getLive() {
return this.client.get(ENDPOINTS.LIVE);
}
/**
* Get YouTube live streams
* @returns {Promise<Object>} YouTube live streams
*/
async getYoutubeLive() {
return this.client.get(ENDPOINTS.LIVE_YOUTUBE);
}
/**
* Get IDN live streams
* @returns {Promise<Object>} IDN live streams
*/
async getIdnLive() {
return this.client.get(ENDPOINTS.LIVE_IDN);
}
/**
* Get Showroom live streams
* @returns {Promise<Object>} Showroom live streams
*/
async getShowroomLive() {
return this.client.get(ENDPOINTS.LIVE_SHOWROOM);
}
/**
* Get recent streams
* @returns {Promise<Object>} Recent streams
*/
async getRecent() {
return this.client.get(ENDPOINTS.RECENT);
}
/**
* Get recent stream detail
* @param {string} liveId - Live stream ID
* @returns {Promise<Object>} Recent stream details
*/
async getRecentDetail(liveId) {
return this.client.get(ENDPOINTS.RECENT_DETAIL(liveId));
}
/**
* Get replay streams
* @returns {Promise<Object>} Replay streams
*/
async getReplay() {
return this.client.get(ENDPOINTS.REPLAY);
}
/**
* Get chat stream
* @param {string} username - Username
* @param {string} slug - Stream slug
* @returns {Promise<Object>} Chat stream data
*/
async getChatStream(username, slug) {
return this.client.get(ENDPOINTS.CHAT_STREAM(username, slug));
}
/**
* Get Showroom chat stream
* @param {string} roomId - Room ID
* @returns {Promise<Object>} Showroom chat stream data
*/
async getChatStreamShowroom(roomId) {
return this.client.get(ENDPOINTS.CHAT_STREAM_SR(roomId));
}
}
/**
* Service for events and theater related API endpoints
*/
class EventsService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get all events
* @returns {Promise<Object>} List of events
*/
async getEvents() {
return this.client.get(ENDPOINTS.EVENTS);
}
/**
* Get theater schedule
* @returns {Promise<Object>} Theater schedule
*/
async getTheater() {
return this.client.get(ENDPOINTS.THEATER);
}
/**
* Get theater detail
* @param {string} id - Theater ID
* @returns {Promise<Object>} Theater details
*/
async getTheaterDetail(id) {
return this.client.get(ENDPOINTS.THEATER_DETAIL(id));
}
/**
* Get video call schedules
* @param {string} sesi - Session
* @param {string} date - Date
* @param {string} member - Member name
* @returns {Promise<Object>} Video call schedules
*/
async getVideoCall(sesi = '', date = '', member = '') {
return this.client.get(ENDPOINTS.VIDEO_CALL(sesi, date, member));
}
/**
* Get today's video call schedules
* @returns {Promise<Object>} Today's video call schedules
*/
async getVideoCallToday() {
return this.client.get(ENDPOINTS.VIDEO_CALL_TODAY);
}
}
/**
* Service for media related API endpoints
*/
class MediaService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get YouTube videos
* @returns {Promise<Object>} YouTube videos
*/
async getYoutube() {
return this.client.get(ENDPOINTS.YOUTUBE);
}
/**
* Get news articles
* @returns {Promise<Object>} News articles
*/
async getNews() {
return this.client.get(ENDPOINTS.NEWS);
}
/**
* Get news detail
* @param {string} id - News ID
* @returns {Promise<Object>} News details
*/
async getNewsDetail(id) {
return this.client.get(ENDPOINTS.NEWS_DETAIL(id));
}
}
/**
* Service for admin related API endpoints
* Requires admin credentials to be set in the client
*/
class AdminService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get all API keys
* @returns {Promise<Object>} List of all API keys
*/
async getKeys() {
return this.client.get(ENDPOINTS.ADMIN_KEYS);
}
/**
* Get API key details
* @param {string} key - API key
* @returns {Promise<Object>} API key details
*/
async getKeyDetail(key) {
return this.client.get(ENDPOINTS.ADMIN_KEY_DETAIL(key));
}
/**
* Create new API key
* @param {string} owner - Owner name
* @param {string} email - Owner email
* @param {string} type - Key type
* @param {string} apikey - Custom API key (optional)
* @returns {Promise<Object>} Created API key
*/
async createKey(owner, email, type = '', apikey = '') {
return this.client.post(ENDPOINTS.ADMIN_CREATE_KEY(owner, email, type, apikey));
}
/**
* Update API key
* @param {string} key - API key
* @param {boolean} active - Active status
* @param {string} type - Key type
* @returns {Promise<Object>} Updated API key
*/
async updateKey(key, active, type) {
return this.client.put(ENDPOINTS.ADMIN_UPDATE_KEY(key, active, type));
}
/**
* Delete API key
* @param {string} key - API key
* @returns {Promise<Object>} Deletion result
*/
async deleteKey(key) {
return this.client.delete(ENDPOINTS.ADMIN_DELETE_KEY(key));
}
/**
* Add request limit to API key
* @param {string} key - API key
* @param {number} additionalLimit - Additional limit to add
* @returns {Promise<Object>} Updated API key
*/
async addLimit(key, additionalLimit) {
return this.client.post(ENDPOINTS.ADMIN_ADD_LIMIT(key, additionalLimit));
}
/**
* Add expiry days to API key
* @param {string} key - API key
* @param {number} additionalDays - Additional days to add
* @returns {Promise<Object>} Updated API key
*/
async addExpiry(key, additionalDays) {
return this.client.post(ENDPOINTS.ADMIN_ADD_EXPIRY(key, additionalDays));
}
/**
* Get admin statistics
* @returns {Promise<Object>} Admin statistics
*/
async getStats() {
return this.client.get(ENDPOINTS.ADMIN_STATS);
}
}
/**
* Service for changelog related API endpoints
*/
class ChangelogService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get all changelogs
* @returns {Promise<Object>} List of changelogs
*/
async getChangelogs() {
return this.client.get(ENDPOINTS.CHANGELOGS);
}
/**
* Get changelog detail
* @param {string} id - Changelog ID
* @returns {Promise<Object>} Changelog details
*/
async getChangelogDetail(id) {
return this.client.get(ENDPOINTS.CHANGELOG_DETAIL(id));
}
/**
* Create new changelog
* @param {Object} data - Changelog data
* @returns {Promise<Object>} Created changelog
*/
async createChangelog(data) {
return this.client.post(ENDPOINTS.CREATE_CHANGELOG, data);
}
/**
* Update changelog
* @param {string} id - Changelog ID
* @param {Object} data - Updated changelog data
* @returns {Promise<Object>} Updated changelog
*/
async updateChangelog(id, data) {
return this.client.put(ENDPOINTS.UPDATE_CHANGELOG(id), data);
}
/**
* Delete changelog
* @param {string} id - Changelog ID
* @returns {Promise<Object>} Deletion result
*/
async deleteChangelog(id) {
return this.client.delete(ENDPOINTS.DELETE_CHANGELOG(id));
}
}
/**
* Service for Grow A Garden game related API endpoints
*/
class GardenService {
constructor(httpClient) {
this.client = httpClient;
}
/**
* Get stock information
* @returns {Promise<Object>} Stock information
*/
async getStock() {
return this.client.get(ENDPOINTS.STOCK);
}
/**
* Get weather information
* @returns {Promise<Object>} Weather information
*/
async getWeather() {
return this.client.get(ENDPOINTS.WEATHER);
}
/**
* Get restock timer
* @returns {Promise<Object>} Restock timer
*/
async getRestockTimer() {
return this.client.get(ENDPOINTS.RESTOCK);
}
/**
* Get all garden information
* @returns {Promise<Object>} All garden data
*/
async getAll() {
return this.client.get(ENDPOINTS.ALL);
}
}
/**
* JKT48 Connect API Client
*/
class JKT48Client {
/**
* Initialize JKT48 API Client
* @param {Object} config - Configuration object
* @param {string} config.apiKey - API key (required)
* @param {string} config.priorityToken - Priority token (optional)
* @param {Object} config.adminCredentials - Admin credentials (optional)
* @param {string} config.adminCredentials.username - Admin username
* @param {string} config.adminCredentials.password - Admin password
*/
constructor(config) {
if (!config || !config.apiKey) {
throw new Error('API key is required');
}
this.httpClient = new HttpClient(
config.apiKey,
config.priorityToken || null,
config.adminCredentials || null
);
// Initialize services
this.members = new MembersService(this.httpClient);
this.live = new LiveService(this.httpClient);
this.events = new EventsService(this.httpClient);
this.media = new MediaService(this.httpClient);
this.admin = new AdminService(this.httpClient);
this.changelog = new ChangelogService(this.httpClient);
this.garden = new GardenService(this.httpClient);
}
/**
* Check API status
* @returns {Promise<Object>} API status
*/
async check() {
return this.httpClient.get(ENDPOINTS.CHECK);
}
/**
* Update API key
* @param {string} newApiKey - New API key
*/
setApiKey(newApiKey) {
this.httpClient.apiKey = newApiKey;
}
/**
* Update priority token
* @param {string} newPriorityToken - New priority token
*/
setPriorityToken(newPriorityToken) {
this.httpClient.priorityToken = newPriorityToken;
}
/**
* Update admin credentials
* @param {Object} credentials - Admin credentials
* @param {string} credentials.username - Admin username
* @param {string} credentials.password - Admin password
*/
setAdminCredentials(credentials) {
this.httpClient.adminCredentials = credentials;
}
}
export { JKT48Client as default };