imessage-ts
Version:
TypeScript library for interacting with iMessage on macOS - send messages, monitor chats, and automate responses
160 lines • 5.64 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Database = void 0;
const path_1 = __importDefault(require("path"));
const os_1 = __importDefault(require("os"));
const fs_extra_1 = __importDefault(require("fs-extra"));
const sqlite3_1 = __importDefault(require("sqlite3"));
const util_1 = require("util");
/**
* Default paths to the iMessage database and attachments
*/
const DEFAULT_DB_PATH = path_1.default.join(os_1.default.homedir(), 'Library/Messages/chat.db');
const DEFAULT_ATTACHMENTS_PATH = path_1.default.join(os_1.default.homedir(), 'Library/Messages/Attachments');
/**
* Class to handle access to the iMessage database
*/
class Database {
constructor(options = {}) {
this.sqliteDb = null;
this.options = {
databasePath: options.databasePath || DEFAULT_DB_PATH,
attachmentsPath: options.attachmentsPath || DEFAULT_ATTACHMENTS_PATH,
pollInterval: options.pollInterval || 1000,
catchUpOnStart: options.catchUpOnStart ?? false,
};
}
/**
* Initialize the database connection
*/
async initialize() {
if (this.sqliteDb) {
return;
}
try {
// Check if the database exists
if (!(await fs_extra_1.default.pathExists(this.options.databasePath))) {
throw new Error(`iMessage database not found at ${this.options.databasePath}`);
}
// Open a direct connection to the database in read-only mode
// No need to copy the file, just connect directly
console.log(`Connecting directly to database at: ${this.options.databasePath}`);
this.sqliteDb = new sqlite3_1.default.Database(this.options.databasePath, sqlite3_1.default.OPEN_READONLY);
// Wait for the database to open
await new Promise((resolve, reject) => {
this.sqliteDb.on('open', () => {
console.log('Database connection opened successfully');
resolve();
});
this.sqliteDb.on('error', (err) => {
console.error('Database connection error:', err);
reject(err);
});
});
}
catch (error) {
throw new Error(`Failed to initialize iMessage database: ${error.message}`);
}
}
/**
* Close the database connection
*/
close() {
if (this.sqliteDb) {
this.sqliteDb.close();
this.sqliteDb = null;
}
}
/**
* Execute a SQL query and return the results
*/
async query(sql, params = {}) {
if (!this.sqliteDb) {
await this.initialize();
}
try {
// Convert named parameters to positional parameters
const formattedSql = this.formatSqlForSqlite3(sql);
const formattedParams = this.formatParamsForSqlite3(formattedSql, params);
// Promisify the all method
const all = (0, util_1.promisify)((sql, params, callback) => {
this.sqliteDb.all(sql, params, callback);
});
return await all(formattedSql, formattedParams);
}
catch (error) {
throw new Error(`Database query failed: ${error.message}`);
}
}
/**
* Execute a SQL query and return a single result
*/
async queryOne(sql, params = {}) {
if (!this.sqliteDb) {
await this.initialize();
}
try {
// Convert named parameters to positional parameters
const formattedSql = this.formatSqlForSqlite3(sql);
const formattedParams = this.formatParamsForSqlite3(formattedSql, params);
// Promisify the get method
const get = (0, util_1.promisify)((sql, params, callback) => {
this.sqliteDb.get(sql, params, callback);
});
return await get(formattedSql, formattedParams) || null;
}
catch (error) {
throw new Error(`Database query failed: ${error.message}`);
}
}
/**
* Format SQL for sqlite3 (replacing $ params with ?)
*/
formatSqlForSqlite3(sql) {
return sql.replace(/\$(\w+)/g, '?');
}
/**
* Format params for sqlite3 (turns named params into positional params)
*/
formatParamsForSqlite3(sql, params) {
const paramNames = sql.match(/\?/g);
if (!paramNames)
return [];
const paramValues = [];
// Extract original param names from the original query
const originalParamNames = Object.keys(params);
// For each ? in the query, find the corresponding parameter value
for (let i = 0; i < paramNames.length; i++) {
if (i < originalParamNames.length) {
paramValues.push(params[originalParamNames[i]]);
}
else {
paramValues.push(null);
}
}
return paramValues;
}
/**
* Get the database path
*/
getDatabasePath() {
return this.options.databasePath;
}
/**
* Get the attachments path
*/
getAttachmentsPath() {
return this.options.attachmentsPath;
}
/**
* Get the poll interval
*/
getPollInterval() {
return this.options.pollInterval;
}
}
exports.Database = Database;
//# sourceMappingURL=database.js.map