Commit 6b787ae0 authored by Iván Sánchez Ortega's avatar Iván Sánchez Ortega

More WIP: account commands, initialization from config file, architecture for...

More WIP: account commands, initialization from config file, architecture for multi-platform support
parent 535c8ebc
// 🍂class Account
// A base abstract class for all social platforms. It's possible to use more than
// an instance of the same `Platform`, e.g. more than one account in a social
// network.
export default class Account {
// 🍂constructor constructor(callback: Function, opts: Object)
// Any platform will be initialized with a `callback` function, that will
// receive the emitted status updates.
constructor(callback, opts) {
if (this._opts === undefined) {
this._opts = {};
}
this._callback = callback;
}
get opts() {
return this._opts;
}
set opts(newOpts) {
return this._opts = Object.assign(this._opts, newOpts);
}
// 🍂method listen(callback: Function, channel: String): this
// Starts listening for updates at the given "channel". A "channel" is a
// string with semantics depending on the platform. A platform might have
// channels like "wall", "stream", "replies", "dms", lists, and so on.
listen(channel) {
return this;
}
// 🍂method listen(channel: String): this
// Stops listening for updates at the given channel. Corresponds to a previous
// `listen()` call.
unlisten(channel) {
return this;
}
// 🍂method listChannels(): Array
// Returns a list of *well-known* channels for this account. This doesn't
// mean that these channels are the only ones available.
listChannels() {
return [];
}
// 🍂section Internal methods
// 🍂method emit(data: Status data, metadata: Object): this
// Makes this `Platform` emit a status update to be printed on the CLI (after
// filtering/transforming by plugins). Needs some normalized data about the
// update, but can include optional metadata as given from the platform.
// This is meant to be called only from subclasses.
emit(data) {
this._callback(this, data);
return this;
}
}
/*
*
🍂namespace Status data
A status update is a plain object with at least the following properties:
🍂property str: String
The main text string of the status update.
🍂property author?: String
An optional string with the author of the update, relative to the platform. Can be empty.
🍂property channel?: String
The channel within the platform that this update came through. Might be empty
if the platform has a "default" channel, or for system messages from the platform.
🍂property realtime?: Boolean
Optionally denotes whether the update is happening in real time (the update has been received
as quick as possible), or not.
🍂property medadata?: Object
An optional dictionary or data structure with platform-specific information. e.g.
the original tweet JSON for twitter, etc.
*/
import Platform from './platform';
/*
* This is a demo platform module.
*
* Every platform is a ES2015 module which must expose:
* - Account (a subclass of 'Account'; a bit confusing, I know)
* - name (a String)
* - signUp (a function that must return a Promise to a set of config options
* for a new configured account)
*/
// 🍂class Clock
// 🍂inherits Platform
import Account from '../abstracts/account';
// 🍂class ClockAccount
// 🍂inherits Account
// A dumb platform which displays timestamps.
export default class Clock extends Platform {
class ClockAccount extends Account {
constructor(callback, opts) {
this._timers = {};
......@@ -60,6 +74,33 @@ export default class Clock extends Platform {
return this;
}
listChannels() {
return ['day', 'hour', 'quarter', 'minute'];
}
}
// signUp returns a Promise for a set of options. If the promise resolves, these options
// will be saved into the user preferences, then a new instance of this platform's `Account`
// will be instantiated.
export function signUp() {
// There is nothing asynchronous here, so...
/// TODO: Extend this with a prompt for the default channel to listen to, or a prompt for
/// the badge.
return Promise.resolve({
});
}
export const name = 'clock';
export { ClockAccount as Account };
export Clock from './clock';
import * as clock from './clock';
export { clock };
......@@ -57,8 +57,8 @@ export default class Platform {
// filtering/transforming by plugins). Needs some normalized data about the
// update, but can include optional metadata as given from the platform.
// This is meant to be called only from subclasses.
emit(data, metadata) {
this._callback(this, data, metadata);
emit(data) {
this._callback(this, data);
return this;
}
}
......@@ -81,6 +81,10 @@ if the platform has a "default" channel, or for system messages from the platfor
Optionally denotes whether the update is happening in real time (the update has been received
as quick as possible), or not.
🍂property medadata?: Object
An optional dictionary or data structure with platform-specific information. e.g.
the original tweet JSON for twitter, etc.
*/
......
import Clock from './platforms/clock';
// import Clock from './platforms/clock';
import * as platforms from './platforms/index'
import Vorpal from 'vorpal';
import Configstore from 'configstore';
import chalk from 'chalk256';
import Flatten from 'flatten-obj';
const flatten = Flatten();
const vorpal = Vorpal();
vorpal.log(chalk.purple('>>> Initializing soCLIal...'));
// create a Configstore instance with an unique ID e.g.
// Package name and optionally some default values
......@@ -23,9 +27,7 @@ const conf = new Configstore('soclial', {
}
});
const vorpal = Vorpal();
vorpal.log(chalk.purple('>>> User configuration file read.'));
function handle(platform, data, metadata) {
......@@ -52,40 +54,91 @@ function handle(platform, data, metadata) {
vorpal.log(
'[' + chalk.foreground(timestampColour)(timestampString) + '] ' +
chalk.foreground(badgeColour)(badge + '/' + channel) +
chalk.foreground(badgeColour)(badge + '/' + channel) +
': ' + chalk.foreground(updateColour)(str)
);
}
// let myClock = new Clock(handle);
let myClock = new Clock(handle, {badge: 'clock'});
myClock.listen('minute');
myClock.listen(10);
// Add a mock 'clock' account to the config
// conf.set('accounts.clock', {
// test: { // alias for the account
// channels: {
// minute: { /* channel-specific config */ },
// '10': { /* channel-specific config */ },
// },
//
// /* other account-specific config */
// badge: 'clock'
// }
// });
// Iterate through the platforms/accounts/channels in the config, and go enabling them.
vorpal.log(chalk.purple('>>> Initializing social accounts...'));
let allConf = conf.all;
// These define the state of the application in this global scope.
let userPlatforms = {};
for (let pl in allConf.accounts) {
userPlatforms[pl] = {};
for (let acc in allConf.accounts[pl]) {
vorpal.log(
chalk.purple('>>> Platform ') +
chalk.cyan(pl) +
chalk.purple(', account ') +
chalk.cyan(acc) +
chalk.purple('...')
);
if (!(pl in platforms)) {
vorpal.error(chalk.red('>>> Unknown platform: ') + chalk.cyan(pl));
continue;
}
userPlatforms[pl][acc] = new platforms[pl].Account(handle, allConf.accounts[pl][acc]);
for (let ch in allConf.accounts[pl][acc].channels) {
vorpal.log(
chalk.purple('>>> Listening to channel ') +
chalk.cyan(ch) +
chalk.purple('...')
);
userPlatforms[pl][acc].listen(ch);
}
}
}
// Command: foo
// Command: config list
vorpal
.command('foo', 'Outputs "bar".')
.command('config list', 'Lists the current configuration values.')
.action(function(args, callback) {
this.log('bar');
this.log( chalk.purple('>>> Listing current configuration:'));
let all = flatten(conf.all);
for (let i in all) {
this.log( chalk.cyan(i) + ': ' + chalk.cyan(all[i]))
}
callback();
});
// Command: config
// Command: config set
vorpal
.command('config [key] [value]', 'Sets a configuration value, or outputs the current configuration values when no configuration key is specified.')
.command('config set [key] [value]', 'Sets a configuration value.')
.action(function(args, callback) {
if (args.key === undefined) {
this.log( chalk.purple('>>> Listing current configuration:'));
let all = flatten(conf.all);
for (let i in all) {
this.log( chalk.cyan(i) + ': ' + chalk.cyan(all[i]))
}
} else if (args.value) {
if (args.key === undefined || args.value === undefined ) {
this.log( chalk.red('>>> Specify configuration key and value to be set.'));
} else {
this.log(
chalk.purple('>>> Setting configuration value ') +
chalk.cyan(args.key) +
......@@ -95,7 +148,21 @@ vorpal
chalk.cyan(args.value)
);
conf.set(args.key, args.value);
}
callback();
});
// Command: config delete
vorpal
.command('config delete [key]', 'Deletes a configuration value.')
.action(function(args, callback) {
if (args.key === undefined ) {
this.log( chalk.red('>>> Specify configuration key to be deleted.'));
} else {
this.log(
chalk.purple('>>> Deleting configuration value ') +
chalk.cyan(args.key)
......@@ -108,6 +175,76 @@ vorpal
// Command: accounts list
vorpal
.command('accounts list', 'Lists the accounts registered.')
.action(function(args, callback) {
this.log( chalk.purple('>>> Listing registered accounts:'));
let allConf = conf.all;
for (let pl in allConf.accounts) {
userPlatforms[pl] = {};
for (let acc in allConf.accounts[pl]) {
vorpal.log(
chalk.purple('>>> Platform ') +
chalk.cyan(pl) +
chalk.purple(', account ') +
chalk.cyan(acc) +
chalk.purple('.')
);
}
}
callback();
});
// Command: accounts delete
vorpal
.command('accounts delete [platform] [alias]', 'Removes an account given its platform and alias.')
.action(function(args, callback) {
if (args.platform === undefined || args.alias === undefined ) {
this.log( chalk.red('>>> Specify platform and alias of account to be deleted.'));
} else {
this.log( chalk.red.bold('>>> NOT IMPLEMENTED YET.'));
/// TODO: Delete config values
/// TODO: Unlisten to all channels
/// TODO: Destroy instance
}
callback();
});
// Command: accounts delete
vorpal
.command('accounts add [platform] [alias]', 'Adds an account given its platform and alias. Most likely you want the alias to be your username in that platform.')
.action(function(args, callback) {
if (args.platform === undefined || args.alias === undefined ) {
this.log( chalk.red('>>> Specify platform and alias of the account.'));
} else {
this.log( chalk.red.bold('>>> NOT IMPLEMENTED YET.'));
}
callback();
});
vorpal.log( chalk.white.bold('>>> Welcome to soCLIal !!!') );
vorpal.log( chalk.white.bold('>>> Type "help" to see available commands.') );
// Init main loop.
vorpal
.delimiter('soCLIal> ')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment