Last active
January 5, 2024 03:47
-
-
Save donpdonp/e6b0da4f4876e9422008c33f8f829f51 to your computer and use it in GitHub Desktop.
gluon simple price watch
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
setup() | |
return { | |
name: "price" | |
} | |
}) | |
var dbkey = "price" | |
var alert_channel = "#zrobo" | |
var state = { watches: {} } | |
var last = {} | |
var etherscan_apikey | |
function setup() { | |
// etherscan key | |
db.get('coin:etherscan_apikey', function(key){ | |
if(key) { | |
etherscan_apikey = key | |
bot.say(bot.admin_channel, 'etherscan apikey loaded') | |
} else { | |
//bot.say(bot.admin_channel, 'coinmarketcap apikey missing') | |
} | |
}) | |
// reload state | |
db.get(dbkey, function(json) { | |
try { | |
state = JSON.parse(json) | |
Object.keys(state.watches).forEach(function(ticker){ | |
var watch = state.watches[ticker] | |
last[ticker] = price_get(ticker) | |
var msg = ticker + " " + watch.formula | |
if (watch.price) { | |
msg = msg + " last: "+watch.price.current_price.usd+" "+watch.price.last_updated | |
} | |
bot.say(bot.admin_channel, msg) | |
}) | |
} catch(e) { | |
bot.say(bot.admin_channel, "price watches decode failed. resetting") | |
save() | |
} | |
}) | |
} | |
function go(msg) { | |
if (msg.method == "clocktower") { | |
var time = new Date(Date.parse(msg.params.time)) | |
if (time.getMinutes() % 10 == 0) { | |
clock_watches() | |
} | |
} | |
if(msg.method == "irc.privmsg") { | |
var match = /^\!price(\s+(.+))?$/.exec(msg.params.message) | |
if(match){ | |
cmd(match[2], msg.params) | |
} | |
} | |
} | |
function clock_watches() { | |
Object.keys(state.watches).forEach(function(ticker){ | |
var watch = state.watches[ticker] | |
var price = price_get(ticker) | |
if (price) { | |
var result = eval(watch, price) | |
if (result.go) { | |
bot.say(watch.channel, watch.nick+": "+result.msg+" ["+ticker+" "+watch.formula+"]") | |
state.watches[ticker].price = price | |
save() | |
} | |
} | |
}) | |
} | |
function cmd(line, params) { | |
var parts = line ? line.split(' ') : [] | |
if (parts.length == 2) { | |
if (parts[1] == 0) { | |
del(parts[0]) | |
bot.say(params.channel, "deleted "+parts[0]) | |
} else { | |
add(params.nick, params.channel, parts[0], parts[1]) | |
bot.say(params.channel, "added "+parts[0]+" "+parts[1]) | |
} | |
} | |
if (parts.length == 1) { | |
var coin = price_get(parts[0]) | |
bot.say(params.channel, ""+parts[0]+" found "+coin.name+"("+coin.symbol+") $"+coin.current_price.usd) | |
} | |
if (parts.length == 0) { | |
if (Object.keys(state.watches).length > 0) { | |
Object.keys(state.watches).forEach(function(ticker){ | |
var watch = state.watches[ticker] | |
var price = price_get(ticker) | |
var result = eval(watch, price) | |
bot.say(params.channel, "["+ticker+" "+watch.formula+"] "+result.msg) | |
}) | |
} else { | |
bot.say(params.channel, "watch list empty. !price [<token> [<rule>]]. rule = >num, <num, num%") | |
} | |
} | |
} | |
function del(ticker) { | |
delete state.watches[ticker] | |
save() | |
} | |
function add(nick, channel, ticker, formula) { | |
state.watches[ticker] = {nick: nick, channel: channel, formula: formula, price: price_get(ticker)} | |
save() | |
} | |
function save() { | |
db.set(dbkey, JSON.stringify(state), function(result){ | |
if (!result) { | |
bot.say(bot.admin_channel, "price state save err: "+e) | |
} | |
}) | |
} | |
function eval(watch, market) { | |
var formula = watch.formula | |
var price = market.current_price.usd | |
var go = false | |
var msg = "" | |
if (formula[0] == ">") { | |
var limit = formula.substring(1); | |
go = price > limit | |
var percent = ((price / limit)-1)*100 | |
msg = " $"+price+" "+percent.toFixed(1)+"% chg" | |
} | |
if (formula[0] == "<") { | |
var limit = formula.substring(1); | |
go = price < limit | |
var percent = ((price / limit)-1)*100 | |
msg = " $"+price+" "+percent.toFixed(1)+"% chg" | |
} | |
if (formula[formula.length-1] == "%") { | |
var limit = watch.price.current_price.usd | |
var limit_percent = formula.substring(0, formula.length-1) | |
var chg = price - limit | |
var chg_sign = chg > 0 ? "+" : "" | |
var chg_ratio = (price - limit) / limit | |
go = Math.abs(chg_ratio) > limit_percent / 100 | |
var ago = time_since(new Date(watch.price.last_updated)) | |
var log10 = Math.log(price) / Math.log(10) | |
var fixed = 6 - (log10 > 0 ? log10 : 0) | |
if (fixed < 0) { fixed = 0 } | |
if (fixed > 20) { fixed = 20 } | |
msg = price + | |
" chg: "+chg_sign+chg.toFixed(fixed)+" ("+(chg_ratio*100).toFixed(1)+"%)"+ | |
" last: "+watch.price.current_price.usd+" ago: "+ago | |
} | |
return {go: go, msg: msg} | |
} | |
function time_since(then) { | |
var now = new Date() | |
var elapsed_sec = (now - then)/1000 | |
var temp | |
temp = elapsed_sec / 60 / 60 / 24 | |
if (temp >= 1) { | |
return temp.toFixed(1)+" days" | |
} | |
temp = elapsed_sec / 60 / 60 | |
if (temp >= 1) { | |
return temp.toFixed(1)+" hours" | |
} | |
temp = elapsed_sec / 60 | |
if (temp >= 1) { | |
return temp.toFixed(1)+" minutes" | |
} | |
return elapsed_sec.toFixed(1)+" seconds" | |
} | |
function price_get(ticker) { | |
if (ticker == "eth:gas") { | |
var gas = eth('gas') | |
return { current_price: {usd: gas.FastGasPrice}, | |
last_updated: new Date()} | |
} else if (ticker == "feargreed") { | |
// curl https://coinmarketcap.com/ | grep NEXT_DATA | cut -c82- | jq .props.pageProps.pageSharedData.fearGreedIndexData.currentIndex | |
// {"score": 57.45799120387904, "maxScore": 100, "name": "Neutral", "updateTime": "2023-07-18T00:00:00.000Z" } | |
} else if (ticker == "allbridge:usdc") { | |
var ab_price = allbridge('usdc') | |
return { current_price: {usd: ab_price}, | |
last_updated: new Date()} | |
} else { | |
var url = "https://api.coingecko.com/api/v3/coins/"+ticker // limit=100 (maximum and default) | |
try { | |
var reply = http.get({url: url}) | |
if (reply.status == 200) { | |
var report = JSON.parse(reply.body) | |
return report.market_data | |
} else { | |
//bot.say(bot.admin_channel, JSON.stringify(reply)) // status is undefined? | |
//bot.say(bot.admin_channel, "price coingecko http "+reply.status+" "+url) | |
} | |
} catch(e) { | |
//bot.say(bot.admin_channel, "price fetch err "+e) | |
} | |
} | |
} | |
function allbridge(ticker) { | |
var url = "https://core.api.allbridgecoreapi.net/token-info" | |
//bot.say(bot.admin_channel, url) | |
try { | |
var json = http.get(url) | |
var data = JSON.parse(json) | |
var tokens = data['ETH']['tokens'].filter(function(c){return c.symbol.toLowerCase() == ticker.toLowerCase() }) | |
if (tokens.length > 0) { | |
//var value = parseFloat(tokens[0]['apr']) *100 | |
var value = parseFloat(tokens[0]['poolInfo']['totalLpAmount']) | |
return value | |
} | |
} catch(e) { | |
bot.say(bot.admin_channel, "allbridge api query failed "+e+" "+url) | |
} | |
} | |
function eth(cmd) { | |
switch(cmd) { | |
case 'gas': | |
var url = 'https://api.etherscan.io/api?module=gastracker&action=gasoracle&apikey='+etherscan_apikey | |
var json = http.get(url) | |
var data = JSON.parse(json) | |
// {"status":"1","message":"OK","result":{"LastBlock":"17238385","SafeGasPrice":"156","ProposeGasPrice":"156","FastGasPrice":"160","suggestBaseFee":"155.096570607","gasUsedRatio":"0.808357866666667,0.628340933333333,0.2736735,0.8038242,0.367055133333333"}} | |
return data.result | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment