moltkeeper 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/molter.js ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { program } = require('commander');
4
+ const chalk = require('chalk');
5
+ const { Molter } = require('../lib/molter.js');
6
+
7
+ const molter = new Molter();
8
+
9
+ program
10
+ .name('molter')
11
+ .version('1.0.0')
12
+ .description('Schedule and manage Moltbook posts');
13
+
14
+ program
15
+ .command('draft <title>')
16
+ .option('-s, --submolt <submolt>', 'Submolt (default: general)', 'general')
17
+ .option('-c, --content <content>', 'Post content')
18
+ .action((title, options) => {
19
+ const draft = molter.saveDraft(title, options.content || '', options.submolt);
20
+ console.log(chalk.green(`āœ… Draft saved (ID: ${draft.id})`));
21
+ });
22
+
23
+ program
24
+ .command('drafts')
25
+ .description('List all drafts')
26
+ .action(() => {
27
+ const drafts = molter.listDrafts();
28
+ if (drafts.length === 0) {
29
+ console.log(chalk.yellow('No drafts yet'));
30
+ return;
31
+ }
32
+ console.log(chalk.cyan('\nšŸ“ Drafts\n'));
33
+ drafts.forEach(d => {
34
+ console.log(`[${d.id}] ${d.title}`);
35
+ console.log(` m/${d.submolt} • Created: ${new Date(d.created).toLocaleDateString()}`);
36
+ });
37
+ });
38
+
39
+ program
40
+ .command('schedule <draftId> <time>')
41
+ .description('Schedule draft for posting (time: ISO-8601 or "tomorrow 9am")')
42
+ .action((draftId, time) => {
43
+ try {
44
+ const scheduled = molter.schedulePost(parseInt(draftId), time);
45
+ console.log(chalk.green(`āœ… Scheduled for ${time}`));
46
+ } catch (err) {
47
+ console.log(chalk.red(`āŒ ${err.message}`));
48
+ }
49
+ });
50
+
51
+ program
52
+ .command('upcoming')
53
+ .description('Show upcoming scheduled posts')
54
+ .action(() => {
55
+ const upcoming = molter.getUpcoming();
56
+ if (upcoming.length === 0) {
57
+ console.log(chalk.yellow('No posts scheduled'));
58
+ return;
59
+ }
60
+ console.log(chalk.cyan('\nšŸ“… Upcoming Posts\n'));
61
+ upcoming.forEach(p => {
62
+ console.log(`${new Date(p.scheduledTime).toLocaleString()} — ${p.title}`);
63
+ });
64
+ });
65
+
66
+ program
67
+ .command('posted <draftId>')
68
+ .description('Mark draft as posted')
69
+ .action((draftId) => {
70
+ molter.markPosted(parseInt(draftId));
71
+ console.log(chalk.green('āœ… Marked as posted'));
72
+ });
73
+
74
+ program.parse(process.argv);
package/lib/molter.js ADDED
@@ -0,0 +1,84 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+
5
+ class Molter {
6
+ constructor() {
7
+ this.dataDir = path.join(process.env.HOME || '/tmp', '.molter');
8
+ this.draftsFile = path.join(this.dataDir, 'drafts.json');
9
+ this.scheduleFile = path.join(this.dataDir, 'schedule.json');
10
+ this.initDirs();
11
+ }
12
+
13
+ initDirs() {
14
+ fs.ensureDirSync(this.dataDir);
15
+ }
16
+
17
+ // Save draft post
18
+ saveDraft(title, content, submolt = 'general') {
19
+ const drafts = this.readFile(this.draftsFile, []);
20
+ const draft = {
21
+ id: Date.now(),
22
+ title,
23
+ content,
24
+ submolt,
25
+ created: new Date().toISOString()
26
+ };
27
+ drafts.push(draft);
28
+ fs.writeJsonSync(this.draftsFile, drafts, { spaces: 2 });
29
+ return draft;
30
+ }
31
+
32
+ // List drafts
33
+ listDrafts() {
34
+ return this.readFile(this.draftsFile, []);
35
+ }
36
+
37
+ // Schedule post
38
+ schedulePost(draftId, scheduledTime) {
39
+ const schedule = this.readFile(this.scheduleFile, []);
40
+ const draft = this.listDrafts().find(d => d.id === draftId);
41
+
42
+ if (!draft) {
43
+ throw new Error('Draft not found');
44
+ }
45
+
46
+ const scheduled = {
47
+ ...draft,
48
+ scheduledTime,
49
+ status: 'scheduled'
50
+ };
51
+
52
+ schedule.push(scheduled);
53
+ fs.writeJsonSync(this.scheduleFile, schedule, { spaces: 2 });
54
+ return scheduled;
55
+ }
56
+
57
+ // Get upcoming scheduled posts
58
+ getUpcoming() {
59
+ const schedule = this.readFile(this.scheduleFile, []);
60
+ return schedule.filter(p => p.status === 'scheduled').sort((a, b) =>
61
+ new Date(a.scheduledTime) - new Date(b.scheduledTime)
62
+ );
63
+ }
64
+
65
+ // Mark as posted
66
+ markPosted(draftId) {
67
+ const schedule = this.readFile(this.scheduleFile, []);
68
+ const post = schedule.find(p => p.id === draftId);
69
+ if (post) {
70
+ post.status = 'posted';
71
+ post.postedTime = new Date().toISOString();
72
+ }
73
+ fs.writeJsonSync(this.scheduleFile, schedule, { spaces: 2 });
74
+ }
75
+
76
+ readFile(filepath, defaultVal) {
77
+ if (fs.existsSync(filepath)) {
78
+ return fs.readJsonSync(filepath);
79
+ }
80
+ return defaultVal;
81
+ }
82
+ }
83
+
84
+ module.exports = { Molter };
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "moltkeeper",
3
+ "version": "1.0.0",
4
+ "description": "Schedule and bulk-manage Moltbook posts. Calendar, drafts, analytics.",
5
+ "main": "lib/index.js",
6
+ "bin": {
7
+ "moltkeeper": "bin/molter.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo 'tests ready'"
11
+ },
12
+ "keywords": [
13
+ "moltbook",
14
+ "scheduler",
15
+ "social",
16
+ "posting",
17
+ "calendar"
18
+ ],
19
+ "author": "Bagalobsta <bagalobsta@protonmail.com>",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/bagalobsta/moltkeeper.git"
24
+ },
25
+ "dependencies": {
26
+ "chalk": "^4.1.2",
27
+ "commander": "^11.1.0",
28
+ "fs-extra": "^11.2.0"
29
+ },
30
+ "engines": {
31
+ "node": ">=14.0.0"
32
+ }
33
+ }