') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - obsantos/MarketBot: Simplest stock market bot for Slack · GitHub
Skip to content

Latest commit

History

36 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

MarketBot

Simplest stock market script & bot for Slack

Instalation

To install dependencies needed run:

$ pip install -r requirements.txt

Script usage

$ python3 stock.py -h
usage: stock.py [-h] -s SYMBOL
Script that queries Yahoo Finance to retrieve a symbol's price
optional arguments:
-h, --help show this help message and exit
-s SYMBOL, --symbol SYMBOL
Symbol you want to query

Example

$ python3 stock.py -s TSLA
Tesla, Inc. (TSLA)
Price: 816.12
Previous: 811.66 - Change: 4.46 (0.55%)

Bot usage

Steps on how to create your Slack App can be found here

After it's all set up you can add the bot to a channel in your slack workspace.

To use it just type any symbol prefixed by the $ sign and the bot will reply with the stock information. (i.e $TSLA, $SBUX)

Like this:

It supports pre/post market notifications as well!

Hosting bot locally

Export the token and signing keys as environment variables:

export SLACK_BOT_TOKEN='YOUR_BOT_TOKEN_HERE'
export SLACK_SIGNING_SECRET='YOUR_SIGNING_KEY_HERE'

Configure ngrok (you could use other service if you prefer) to create a public URL that tunnels the requests to your machine. Steps to configure ngrok can be found here

Make sure you run ngrok specifying the same port as defined in bot.py

Run the bot:

$ python3 bot.py * Serving Flask app "bot" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

After it's all up and running you need to go to Slack Apps -> Features -> Event Subscriptions and enable events as well as adding the ngrok url and include message.channels in the list Subscribe to bot events

Hosting bot in Heroku

If you don't want to host the bot locally you can use the free tier of Heroku to host it. This repo includes the requirements.txt and Procfile needed for Heroku to identify this project as Python app and run it after building it.

More details on how to setup Heroku, push the code, etc can be found here

Don't forget to configure the environment variables:

If you have heroku cli, configure environment variables like this:

heroku config:set SLACK_BOT_TOKEN="YOUR_BOT_TOKEN_HERE"
heroku config:set SLACK_SIGNING_SECRET="YOUR_SIGNING_KEY_HERE"

If you don't want to use the cli they can be configured on the app Dashboard -> Settings -> Config Vars

After it's all up and running you need to go to Slack Apps -> Features -> Event Subscriptions and enable events as well as adding the heroku url and include message.channels in the list Subscribe to bot events

About

Simplest stock market bot for Slack

Resources

Stars

2 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages