Skip to content
This repository was archived by the owner on Aug 10, 2022. It is now read-only.

Commit 5601ea5

Browse files
committed
Fully add BitcoinTrigger
1 parent a7d5ed9 commit 5601ea5

File tree

4 files changed

+144
-84
lines changed

4 files changed

+144
-84
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ General bot-related and chat-related functions.
8282
#### Trade-related triggers
8383

8484
- `BanCheckTrigger` - checks to see if a user has any VAC/economy/community bans via steam API. Requires a steam api key, defined as option apikey in the plugin, or globally defined as a chatBot option steamapikey
85+
- `BitcoinTrigger` - does various actions over the BTC network
8586
- `ProfileCheckTrigger` - When a user joins, look up their profile in steam API and if they have a private profile, or never bothered to set one up, announce it to the groupchat. Optional option: apikey can be defined in options, overriding any steamapikey can be defined in the bot constructor. If neither is defined, it won't be used (not required).
8687
- `RedditTrigger` - Check reddit flair/bans/username on command. Requires access to the /r/SGS API. Note: I will not help you with gaining access to the API, and everything I know about the API is used in this trigger.
8788
- `SteamrepTrigger` - checks steamrep API on command. If Steamrep lists the user as a scammer, then bot announces it and gives links for more info. See `SteamrepOnJoinTrigger` to do this whenever someone joins a chat.

TRIGGERS.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,29 @@ Options:
6161
- command - *string* - defaults to "!ban"
6262
- user - *array* of *string*s. Who can use this trigger
6363

64+
### `BitcoinTrigger`
65+
66+
Does various actions over the BTC network. [Coinbase api](https://www.coinbase.com/settings/api) (create OAuth application). Must have webserver enabled.
67+
68+
Options:
69+
70+
Required:
71+
72+
- clientID - *string* - Coinbase clientID
73+
- clientSecret * string* - Coinbase clientSecret
74+
- redirectURI *string* - redirectURI for OAauth app
75+
76+
Optional:
77+
78+
- clearcommand - *string* - defaults to !clear
79+
- authcommand - *string* - defaults to !auth
80+
- saveTimer - *integer* - database file save interval, defaults to 5 minutes
81+
- sellcommand - *string* - defaults to !sell
82+
- buycommand - *string* - defaults to !buy
83+
- balancecommand - *string* - defaults to !balance
84+
- pricescommand - *string* - defaults to !prices
85+
- dbFile - *string* - database file name
86+
6487
### `BotCommandTrigger`
6588

6689
Runs a specified callback when a specific command message is typed. It is preferred to write an actual trigger, but for simple things (e.g. muting, unmuting), this is easier.

examples/example-config-triggers2.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -328,5 +328,15 @@ module.exports = [
328328
//check steamrep on every user who joins. Notify the channel if they're a scammer.
329329
{ name: 'SteamrepOnJoin',
330330
type: 'SteamrepOnJoinTrigger',
331-
options: {} }
331+
options: {} },
332+
333+
{
334+
name: 'BTC',
335+
type: 'BitcoinTrigger',
336+
options: {
337+
clientID: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
338+
clientSecret: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
339+
redirectURI: 'http://localhost:8080/coinbase/'
340+
}
341+
}
332342
];

lib/triggers/bitcoinTrigger.js

Lines changed: 109 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,16 @@ exports.create = function(name, chatBot, options) {
1919
trigger.options.clientSecret = trigger.options.clientSecret || undefined;
2020
trigger.options.redirectURI = trigger.options.redirectURI || undefined;
2121

22+
trigger.scope = 'wallet:accounts:read,wallet:sells:create';
2223
trigger.db = (function() { try { return JSON.parse(fs.readFileSync(trigger.options.dbFile)); } catch(e) { return {}}})();
23-
trigger.client;
24+
2425
trigger.options.clearcommand = trigger.options.clearcommand || '!clear';
2526
trigger.options.authcommand = trigger.options.authcommand || '!auth';
26-
trigger.options.saveTimer = trigger.options.saveTimer || 1000 * 60 * 30;
27+
trigger.options.saveTimer = trigger.options.saveTimer || 1000 * 60 * 5;
2728
trigger.options.sellcommand = trigger.options.sellcommand || '!sell';
2829
trigger.options.buycommand = trigger.options.buycommand || '!buy';
2930
trigger.options.balancecommand = trigger.options.balancecommand || '!balance';
30-
trigger.options.sendcommand = trigger.options.sendcommand || '!send';
31-
trigger.options.requestcommand = trigger.options.requestcommand || '!request';
3231
trigger.options.pricecommand = trigger.options.pricecommand || '!prices';
33-
trigger.options.transcommand = trigger.options.transcommand || '!transfers';
3432
return trigger;
3533
}
3634

@@ -48,7 +46,7 @@ BitcoinTrigger.prototype._onLoad = function() {
4846
else {
4947
that.winston.debug(withName + 'Wrote to db file');
5048
}
51-
})
49+
});
5250
}
5351
catch (e) {
5452
that.winston.error(withName + 'Error: ' + e);
@@ -58,10 +56,6 @@ BitcoinTrigger.prototype._onLoad = function() {
5856
return true;
5957
}
6058
if(!this.options.clientID || !this.options.clientSecret || !this.options.redirectURI || this.chatBot.options.disableWebserver === true) {
61-
console.log(this.options.clientID);
62-
console.log(this.options.clientSecret);
63-
console.log(this.options.redirectURI);
64-
console.log(this.chatBot.options.disableWebserver);
6559
this.winston.error(withName + 'Must specify client ID and client Secret and redirect URI and webserver enabled!');
6660
return false;
6761
}
@@ -90,9 +84,6 @@ BitcoinTrigger.prototype._respond = function(toId, userId, message) {
9084
}
9185
}
9286

93-
setInterval(function() {
94-
that._refreshToken(userId);
95-
}, 1000 * 60 * 60 * 2);
9687
var query = this._stripCommand(message, this.options.balancecommand);
9788
if(query) {
9889
this._getBalance(toId, userId);
@@ -120,35 +111,99 @@ BitcoinTrigger.prototype._respond = function(toId, userId, message) {
120111
}
121112

122113
query = this._stripCommand(message, this.options.sellcommand);
114+
if(query && query.params.length === 3) {
115+
this._sell(toId, userId, query.params[1], query.params[2]);
116+
}
117+
118+
query = this._stripCommand(message, this.options.buycommand);
119+
if(query && query.params.length === 3) {
120+
this._buy(toId, userId, query.params[1], query.params[2]);
121+
}
122+
123+
query = this._stripCommand(message, this.options.pricecommand);
123124
if(query && query.params.length === 2) {
124-
this._sell(toId, userId, query.params[1]);
125+
this._getPrices(toId, userId, query.params[1]);
125126
}
126127
}
127128

128-
BitcoinTrigger.prototype._sell = function(toId, userId, amount) {
129+
BitcoinTrigger.prototype._getPrices = function(toId, userId, currency) {
130+
const _db = this.db[userId];
131+
const client = new Coinbase.Client({
132+
"accessToken": _db.accessToken,
133+
"refreshToken": _db.refreshToken
134+
});
135+
const withName = this.chatBot.name + '/' + this.name + ': ';
136+
137+
var sellPrice = '';
138+
var buyPrice = '';
139+
140+
client.getBuyPrice({
141+
"currency": currency
142+
}, function(e, obj) {
143+
buyPrice = obj.data.amount;
144+
});
145+
client.getSellPrice({
146+
"currency": currency
147+
}, function(e, obj) {
148+
sellPrice = obj.data.amount;
149+
});
150+
151+
setTimeout(() => {
152+
this._sendMessageAfterDelay(toId, 'Sell price: ' + sellPrice + '. Buy price: ' + buyPrice);
153+
}, 1000);
154+
}
155+
156+
BitcoinTrigger.prototype._buy = function(toId, userId, amount, currency) {
157+
const withName = this.chatBot.name + '/' + this.name + ': ';
158+
const _db = this.db[userId];
159+
const client = new Coinbase.Client({
160+
"accessToken": _db.accessToken,
161+
"refreshToken": _db.refreshToken
162+
});
163+
const account = new Coinbase.model.Account(client, {
164+
"id": _db.id
165+
});
166+
167+
const args = {
168+
"amount": amount,
169+
"currency": currency
170+
};
171+
172+
account.buy(args, (e, xfer) => {
173+
if(e) {
174+
this.winston.error(withName + e.stack);
175+
this._sendMessageAfterDelay(toId, `Error buying ${amount} ${currency} for user ${userId}: ${e.message}`);
176+
}
177+
else {
178+
this._sendMessageAfterDelay(toId, `${userId} successfully bought ${amount} ${currency}!`);
179+
this._sendMessageAfterDelay(userId, `Transfer ID is ${xfer.id}`);
180+
}
181+
});
182+
}
183+
184+
BitcoinTrigger.prototype._sell = function(toId, userId, amount, currency) {
129185
const that = this;
130186
const withName = this.chatBot.name + '/' + this.name + ': ';
131187
var _db = this.db[userId];
132188
const client = new Coinbase.Client({
133189
"accessToken": _db.accessToken,
134-
"refreshToken": _db.refreshToken,
135-
'baseApiUri': 'https://api.sandbox.coinbase.com/v2/',
136-
'tokenUri': 'https://api.sandbox.coinbase.com/oauth/token'
190+
"refreshToken": _db.refreshToken
137191
});
138192
const account = new Coinbase.model.Account(client, { "id": _db.id });
139193

140-
var args = {
141-
"qty": amount
142-
}
194+
const args = {
195+
"amount": amount,
196+
"currency": currency
197+
};
143198

144199
account.sell(args, function(e, xfer) {
145200
if(e) {
146201
that.winston.error(withName + e.stack);
147-
that._sendMessageAfterDelay(toId, 'Error selling ' + amount + ' bitcoints for user ' + userId + ': ' + e.message);
202+
that._sendMessageAfterDelay(toId, 'Error selling ' + amount + ' ' + currency + ' for user ' + userId + ': ' + e.message);
148203
}
149204
else {
150-
that._sendMessageAfterDelay(toId, userId + ' successfully sold ' + amount + ' bitcoins!');
151-
that._sendMessageAfterDelay(userId, 'Transfer id is ' + xfer.id);
205+
that._sendMessageAfterDelay(toId, userId + ' successfully sold ' + amount + ' ' + currency + '!');
206+
that._sendMessageAfterDelay(userId, 'Transfer ID is ' + xfer.id);
152207
}
153208
});
154209
}
@@ -177,95 +232,67 @@ BitcoinTrigger.prototype._refreshToken = function(userId) {
177232
}
178233

179234
BitcoinTrigger.prototype._getId = function(userId) {
180-
const that = this;
181-
var _db = that.db[userId];
235+
var _db = this.db[userId];
182236
const withName = this.chatBot.name + '/' + this.name + ': ';
183237
const client = new Coinbase.Client({
184238
"accessToken": _db.accessToken,
185-
"refreshToken": _db.refreshToken,
186-
'baseApiUri': 'https://api.sandbox.coinbase.com/v2/',
187-
'tokenUri': 'https://api.sandbox.coinbase.com/oauth/token'
239+
"refreshToken": _db.refreshToken
188240
});
189-
client.getAccounts(function(e, accounts) {
241+
client.getAccounts({}, (e, accounts) => {
190242
if(e) {
191-
that.winston.error(withName + e.stack);
243+
this.winston.error(withName + e.stack);
192244
}
193245
else {
194-
_db.id === account.id;
246+
accounts.forEach(account => {
247+
_db.id = account.id;
248+
});
195249
}
196250
})
197251
}
198252

199253
BitcoinTrigger.prototype._authorize = function(userId, callback) {
200-
const that = this;
201254
const withName = this.chatBot.name + '/' + this.name + ': ';
202255
this.express = this.chatBot.express;
203256
var _url;
204257
var _code;
205258
var accessToken;
206259
var refreshToken;
207-
//var _scope = 'wallet:accounts:read,wallet:accounts:create,wallet:accounts:update,wallet:accounts:delete';
208-
var _scope = 'user,balance,sell';
209260
var uri = "https://sandbox.coinbase.com/oauth/authorize" +
210-
"?response_type=code&redirect_uri=" + encodeURI(that.options.redirectURI) +
211-
"&client_id=" + that.options.clientID + '&scope=' + _scope;
261+
"?response_type=code&redirect_uri=" + encodeURI(this.options.redirectURI) +
262+
"&client_id=" + this.options.clientID + '&scope=' + this.scope;
212263
Request({
213264
uri: uri,
214265
json: true,
215266
followAllRedirects: true
216-
}, function(e, response, body) {
267+
}, (e, response, body) => {
217268
if(e) {
218269
callback(e, null, null);
219270
}
220271
else {
221-
that._sendMessageAfterDelay(userId, 'Please visit the following link and click "Authorize".\n\n' + uri);
272+
this._sendMessageAfterDelay(userId, 'Please visit the following link and click "Authorize".\n\n' + uri);
222273
var last = /^[^\/]*(?:\/[^\/]*){2}/g;
223274
//console.log(that.options.redirectURI.match(last));
224-
var getter = that.options.redirectURI.replace(last, '');
225-
that.chatBot._addRouter(getter);
226-
that.winston.silly(getter);
227-
/*
228-
that.express.get(getter, function(req, res) {
229-
_url = req.url;
230-
_code = _url.match(/\?code=(.*)/i)[1];
231-
res.send('<h1>Response received, you may close this window now.<h2>' + '<br><h4>Your code is ' + _code + '<h4><br>');
232-
Request({
233-
method: "POST",
234-
uri: "https://sandbox.coinbase.com/oauth/token?grant_type=authorization_code&code=" +
235-
_code + "&client_id=" + that.options.clientID + "&client_secret=" +
236-
that.options.clientSecret + "&redirect_uri=" + that.options.redirectURI,
237-
json: true,
238-
followAllRedirects: true
239-
}, function(e, response, body) {
240-
if(!body.access_token) {
241-
callback(e, null, null);
242-
}
243-
else {
244-
accessToken = body.access_token;
245-
refreshToken = body.refresh_token;
246-
callback(null, accessToken, refreshToken);
247-
}
248-
});
249-
});
250-
*/
251-
that.express.use(getter, function(req, res, next) {
275+
var getter = this.options.redirectURI.replace(last, '');
276+
this.chatBot._addRouter(getter);
277+
this.express.use(getter, (req, res, next) => {
252278
_url = req.url;
253279
_code = _url.match(/\?code=(.*)/i)[1];
254280
res.send('<h1>Response received, you may close this window now.<h2>' + '<br><h4>Your code is ' + _code + '<h4><br>');
255281
Request({
256282
method: "POST",
257283
uri: "https://sandbox.coinbase.com/oauth/token?grant_type=authorization_code&code=" +
258-
_code + "&client_id=" + that.options.clientID + "&client_secret=" +
259-
that.options.clientSecret + "&redirect_uri=" + that.options.redirectURI,
284+
_code + "&client_id=" + this.options.clientID + "&client_secret=" +
285+
this.options.clientSecret + "&redirect_uri=" + this.options.redirectURI,
260286
json: true,
261287
followAllRedirects: true
262-
}, function(e, response, body) {
288+
}, (e, response, body) => {
263289
if(!body.access_token) {
264290
callback(e, null, null);
265291
}
266292
else {
267293
accessToken = body.access_token;
268294
refreshToken = body.refresh_token;
295+
this._getId(userId);
269296
callback(null, accessToken, refreshToken);
270297
}
271298
});
@@ -285,32 +312,31 @@ BitcoinTrigger.prototype._getBalance = function(toId, userId) {
285312
var _db = that.db[userId];
286313
var client = new Coinbase.Client({
287314
"accessToken": _db.accessToken,
288-
"refreshToken": _db.refreshToken,
289-
'baseApiUri': 'https://api.sandbox.coinbase.com/v2/',
290-
'tokenUri': 'https://api.sandbox.coinbase.com/oauth/token'
315+
"refreshToken": _db.refreshToken
291316
});
292-
client.getAccounts({}, (e, accounts) => {
317+
client.getAccount(_db.id, (e, account) => {
293318
if(e) {
294319
if(e.type === 'ExpiredAccessToken') {
295-
that._refreshToken(userId);
296-
that._sendMessageAfterDelay(userId, 'Access token expired, refreshing. Please try again in a few seconds.');
297-
that.winston.warn(withName + 'Refreshing access token');
320+
this._refreshToken(userId);
321+
this._sendMessageAfterDelay(userId, 'Access token expired, refreshing. Please try again in a few seconds.');
322+
this.winston.warn(withName + 'Refreshing access token');
298323
}
299-
that.winston.error(e);
300-
that._sendMessageAfterDelay(userId, e.message);
324+
this.winston.error(e.stack);
325+
this._sendMessageAfterDelay(userId, e.message);
301326
}
302327
else {
303-
accounts.forEach(function(acct) {
304-
that._sendMessageAfterDelay(toId, 'The balance for ' + that.chatBot._userString(userId) + ' is ' + acct.balance.amount + ' ' + acct.balance.currency);
305-
})
328+
this._sendMessageAfterDelay(toId, 'The balance for ' + this.chatBot._userString(userId) + ' is ' + account.balance.amount + ' ' + account.balance.currency);
306329
}
307330
})
308331
}
309332
}
310333

311334
BitcoinTrigger.prototype._stripCommand = function(message, command) {
312335
if (command && message && message.toLowerCase().indexOf(command.toLowerCase()) === 0) {
313-
return {message: message, params: message.split(" ")};
336+
return {
337+
message: message,
338+
params: message.split(" ")
339+
};
314340
}
315341
return null;
316342
}

0 commit comments

Comments
 (0)