Skip to content

Instantly share code, notes, and snippets.

@liponex
Created March 9, 2024 11:19
Show Gist options
  • Save liponex/23e2f262789970e0c2261a3fc29de830 to your computer and use it in GitHub Desktop.
Save liponex/23e2f262789970e0c2261a3fc29de830 to your computer and use it in GitHub Desktop.
Notcoin Farming
// ==UserScript==
// @name notcoin-allow-browser
// @namespace http://tampermonkey.net/
// @version 2024-03-09
// @description Passthrough mobile check for notcoin!
// @author liponex (liponex.ru)
// @homepageURL https://gist.github.com/liponex
// @match https://clicker.joincommunity.xyz/*
// @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
// @grant none
// @run-at document-start
// ==/UserScript==
(function() {
window.changeHashPlatform = () => {
var lochash = location.hash.toString();
lochash = lochash.replaceAll("tgWebAppPlatform=weba", "tgWebAppPlatform=android");
location.hash = lochash;
};
window.changeHashPlatform();
addEventListener("hashchange", (event) => {
console.log(event);
window.changeHashPlatform();
});
})();
// ==UserScript==
// @name notcoin-bot
// @namespace http://tampermonkey.net/
// @match https://*.telegram.org/*#@notcoin_bot
// @match https://*.joincommunity.xyz/*
// @grant GM_addValueChangeListener
// @grant GM_removeValueChangeListener
// @grant GM_setValue
// @version 1.0
// @author liponex (liponex.ru)
// @author Denis Tk (dapie.github.io)
// @homepageURL https://gist.github.com/liponex
// ==/UserScript==
// === helpers ===
const delay = (time) => new Promise(r => setTimeout(r, time));
const getReactProps = (component) => component ? Object.entries(component).find(([key]) => key.startsWith("__reactProps$"))[1] : null;
const getIdleTime = () => { return 1250 * 1000; };
const getRandomClickTime = () => Math.floor(Math.random() * 5 + 60);
const waitForElm = (selector, wait = 0) => {
return new Promise((resolve, reject) => {
if (document.querySelector(selector)) {
return resolve(document.querySelector(selector));
}
const observer = new MutationObserver(mutations => {
if (document.querySelector(selector)) {
clearTimeout(timeout);
observer.disconnect();
resolve(document.querySelector(selector));
}
});
const timeout = wait && setTimeout(
() => {
observer.disconnect();
reject('Never showed up. ' + selector)
},
wait
);
observer.observe(document.body, {
childList: true,
subtree: true
});
});
}
let coinCenter = {};
coinCenter.x = 0;
coinCenter.y = 0;
const getEvent = () => ({
touches: [{clientX: Math.random() * 5 + coinCenter.x, clientY: Math.random() * 5 + coinCenter.y}]
})
// === script ===
let shouldCheckBoosts = false;
let intervalId;
let clicksCount = 0;
let countFlag = 100;
const MIN_SCORE = 9;
const MAX_CLICKS_PER_ITERATION = 10000;
const coinClick = async () => {
const coin = document.querySelector('div[class^="_notcoin"]');
const scoreElement = document.querySelector('div[class^="_scoreCurrent"]');
const score = scoreElement ? parseInt(scoreElement.textContent) : null;
if (countFlag > 0) {
let rect = coin.getBoundingClientRect()
coinCenter.x = rect.x + coin.offsetWidth / 2;
coinCenter.y = rect.y + coin.offsetHeight / 2;
countFlag--;
}
// Rocket click
const rocket = document.querySelector('img[class^="_root"]');
const rocketProps = getReactProps(rocket);
if (rocketProps) rocketProps.onClick();
if (intervalId === null || score === null) return;
const isMinScore = score < MIN_SCORE;
const isMaxClicksForIteration = clicksCount < MAX_CLICKS_PER_ITERATION;
const shouldClick = !isMinScore && isMaxClicksForIteration;
// Coin click
if (shouldClick && coin) {
clicksCount++;
const {onTouchStart, onTouchEnd} = getReactProps(coin);
onTouchStart(getEvent());
await delay(10);
onTouchEnd();
} else {
clearInterval(intervalId)
clicksCount = 0;
console.info('%c INFO: idle time', 'color: #64b5f6');
if (isMinScore && shouldCheckBoosts) boostClick()
await delay(getIdleTime());
console.info('%c INFO: trying again', 'color: #64b5f6');
start();
}
}
const start = () => {
intervalId = setInterval(coinClick, getRandomClickTime());
};
const stop = () => {
clearInterval(intervalId);
intervalId = null;
};
const BOOST_CLICK_TIMEOUT = 3000;
const boostClick = async () => {
console.info('%c INFO: open boosts', 'color: #64b5f6');
const buttonGroup = document.querySelector('div[class^="_buttonGroup"]');
const boostButton = buttonGroup && getReactProps(buttonGroup.lastChild);
await delay(BOOST_CLICK_TIMEOUT);
if (!boostButton) return;
boostButton.onClick();
await delay(BOOST_CLICK_TIMEOUT);
const dailyTask = document.querySelector('div[class^="_taskDailyItem"]:not([class*="_completed"])');
const dailyBoost = getReactProps(dailyTask);
const taskCarousel = document.querySelector('div[class^="_taskCarousel"][class*="_willChange"]');
const boost = getReactProps(taskCarousel && taskCarousel.querySelector('div[class*="_rippleEffect"]'));
const boosterButton = dailyBoost || boost;
if (boosterButton) {
boosterButton.onClick();
await delay(BOOST_CLICK_TIMEOUT);
const getButton = getReactProps(document.querySelector('button[class*="_typeBlue"]'));
if(getButton) getButton.onClick();
await delay(BOOST_CLICK_TIMEOUT);
console.info('%c DONE: boosted', 'color: #bada55');
}
console.info('%c INFO: close boosts', 'color: #64b5f6');
history.back();
}
const init = async () => {
console.info('%c INIT: start initialization', 'color: #64b5f6');
await waitForElm('.reply-markup-button').then(() => {
const button = document.querySelector('.reply-markup-button');
button.click();
console.info('%c INIT: initializated', 'color: #64b5f6');
});
}
if(location.host === 'web.telegram.org') {
init();
GM_addValueChangeListener('shouldRefresh', (_name, _oldValue, newValue) => {
console.info('%c INFO: refreshing', 'color: #64b5f6');
if(newValue) location.reload();
})
}
if(location.host === 'clicker.joincommunity.xyz') {
waitForElm('div[class^="_notcoin"]', 10000).then(start).catch(() => {
console.info('%c INFO: not started. reload window', 'color: #64b5f6');
location.reload()
});
GM_setValue('shouldRefresh', false);
const requestRefresh = () => {
console.info('%c INFO: click request failed', 'color: #64b5f6');
GM_setValue('shouldRefresh', true);
}
const {fetch: origFetch} = window.unsafeWindow;
window.unsafeWindow.fetch = async (...args) => {
try {
const response = await origFetch(...args);
if (response && response.status === 401) requestRefresh();
return response;
} catch (error) {
if(!args[0].signal.aborted) {
requestRefresh()
}
throw error
}
};
}
@mkatsovets
Copy link

Is it possible to add a guide on how to use it?

@liponex
Copy link
Author

liponex commented Mar 22, 2024

Is it possible to add a guide on how to use it?

Install Tampermonkey in your browser. Install in it both notcoin-allow-browser.js and notcoin-bot.js.
Then, you must open telegram and open notcoin. The app must start, but sometimes its not. Just click refresh page on app, and this thing must go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment