Advertisement
Multivit4min

Untitled

Mar 3rd, 2020
181
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.32 KB | None | 0 0
  1. "use strict";
  2. ///<reference path="node_modules/sinusbot/typings/global.d.ts" />
  3. registerPlugin({
  4. name: "Vote Reward",
  5. engine: ">= 1.0.0",
  6. version: "1.0.0",
  7. description: "Group Vote Rewards for TeamSpeakServers.org",
  8. author: "Multivitamin <david.kartnaller@gmail.com",
  9. requiredModules: ["http"],
  10. vars: [{
  11. name: "key",
  12. title: "TeamSpeak-Servers API Key",
  13. type: "string",
  14. default: ""
  15. }, {
  16. name: "sid",
  17. title: "TeamSpeak-Servers Server ID",
  18. type: "string",
  19. default: ""
  20. }, {
  21. name: "removeTime",
  22. title: "remove vote when older than x days (default: 30, -1 to disable removal)",
  23. type: "number",
  24. default: 30
  25. }, {
  26. name: "rewards",
  27. title: "Group per vote",
  28. type: "array",
  29. vars: [{
  30. name: "amount",
  31. title: "Amount of Votes in this month",
  32. type: "number",
  33. default: 0
  34. }, {
  35. name: "group",
  36. title: "Group Reward",
  37. type: "number",
  38. default: 0
  39. }]
  40. }]
  41. }, (_, config) => {
  42. const { key, sid, removeTime, rewards } = config;
  43. let initialized = false;
  44. const rewardSorted = rewards.sort((g1, g2) => g1.amount - g2.amount).reverse();
  45. const availableGroups = rewards.map(g => g.group);
  46. const removeAfter = removeTime > 0 ? removeTime * 24 * 60 * 60 : -1;
  47. const engine = require("engine");
  48. const event = require("event");
  49. const store = require("store");
  50. const http = require("http");
  51. const backend = require("backend");
  52. const helpers = require("helpers");
  53. const CHECK_INTERVAL = 60 * 1000;
  54. class Vote {
  55. /** initializes store */
  56. init() {
  57. if (!Array.isArray(this.getVotes()))
  58. this.setVotes([]);
  59. }
  60. /** current store namespace */
  61. ns(name) {
  62. return `${this.namespace}${name}`;
  63. }
  64. /** retrieves the namespace for vote items */
  65. get nsVotes() {
  66. return this.ns("votes");
  67. }
  68. /** retrieves all votes from store */
  69. getVotes() {
  70. return store.getInstance(this.nsVotes);
  71. }
  72. /** saves all vote items back to store */
  73. setVotes(items) {
  74. store.setInstance(this.nsVotes, items);
  75. }
  76. /** retrieves all votes */
  77. saveItem(item) {
  78. this.setVotes(this.getVotes().map(i => i.hash === item.hash ? item : i));
  79. return this;
  80. }
  81. addItem(item) {
  82. this.setVotes([...this.getVotes(), item]);
  83. return this;
  84. }
  85. /** requests to add an item to the store */
  86. requestAdd(item) {
  87. const hash = this.getHash(item);
  88. if (this.findHash(hash))
  89. return false;
  90. if (this.isOld(item))
  91. return false;
  92. const newItem = this.createVoteItem(item);
  93. this.addItem(newItem);
  94. this.tryMakeClaim(newItem);
  95. return true;
  96. }
  97. /** checks if the item is too old to get added or still be hold in store */
  98. isOld(item) {
  99. if (removeAfter === -1)
  100. return false;
  101. return item.timestamp < Math.floor(Date.now() / 1000) - removeAfter;
  102. }
  103. /** retrieves the hash value of an item */
  104. getHash(item) {
  105. return helpers.MD5Sum(`${item.nickname}${item.timestamp}`);
  106. }
  107. /** finds an item with a specific hash */
  108. findHash(hash) {
  109. return this.getVotes().find((item) => item.hash === hash);
  110. }
  111. /** handles a full client check */
  112. checkClient(client) {
  113. this.getUnclaimedByNickname(client.nick()).forEach(item => this.tryMakeClaim(item, client));
  114. this.checkGroups(client);
  115. }
  116. /** tries to claim a possible not claimed item */
  117. tryMakeClaim(item, client) {
  118. if (!this.isUnclaimed(item))
  119. return false;
  120. client = client ? client : this.getClientByItem(item);
  121. if (!client)
  122. return false;
  123. engine.log(`Client ${client.nick()} (${client.uid()}) claims a vote (${item.hash})`);
  124. this.flagItemClaimed(item, client.uid());
  125. this.saveItem(item);
  126. this.checkGroups(client);
  127. return true;
  128. }
  129. /** checks wether an item is unclaimed or not */
  130. isUnclaimed(item) {
  131. return item.claimedBy === null;
  132. }
  133. /** tries to retrieve the client for which the vote is for */
  134. getClientByItem(item) {
  135. return backend.getClientByName(item.nickname);
  136. }
  137. /** validates the groups a client has */
  138. checkGroups(client) {
  139. const group = this.getGroupFromVoteCount(this.getVotesByClient(client).length);
  140. if (group === -1)
  141. return;
  142. return this.whiteListGroup(client, [group], availableGroups);
  143. }
  144. /**
  145. * adds a set of groups to a client and removes groups he should not be in
  146. * @param client the client to add/remove groups from
  147. * @param group the groups a client can have
  148. * @param whitelisted whitelisted groups
  149. */
  150. whiteListGroup(client, groups, whitelisted) {
  151. let assign = groups.map(g => String(g));
  152. const remove = whitelisted.map(w => String(w)).filter(w => !assign.includes(w));
  153. client.getServerGroups().forEach(group => {
  154. if (remove.includes(group.id())) {
  155. client.removeFromServerGroup(group.id());
  156. }
  157. else if (assign.includes(group.id())) {
  158. assign.splice(assign.indexOf(group.id()), 1);
  159. }
  160. });
  161. assign.forEach(g => client.addToServerGroup(g));
  162. }
  163. /**
  164. * retrieves the servergroup the amount of counts should get
  165. * @param votes the votecount to check
  166. */
  167. getGroupFromVoteCount(votes) {
  168. let g = rewardSorted.find(g => g.amount <= votes);
  169. if (!g) {
  170. if (rewardSorted.length === 0 || rewardSorted[0].amount > votes) {
  171. g = { amount: -1, group: -1 };
  172. }
  173. else {
  174. g = rewardSorted[rewardSorted.length - 1];
  175. }
  176. }
  177. return g.group;
  178. }
  179. /**
  180. * retrieves the vote items a client has been assigned
  181. * @param client the client to retrieve
  182. */
  183. getVotesByClient(client) {
  184. return this.getVotes().filter(item => item.claimedBy === client.uid());
  185. }
  186. /**
  187. * gets all unclaimed votes a client nickname can be assigned to
  188. * @param nick the nickname to check
  189. */
  190. getUnclaimedByNickname(nick) {
  191. return this.getVotes()
  192. .filter(item => this.isUnclaimed(item))
  193. .filter(item => item.nickname === nick);
  194. }
  195. /**
  196. * removes all items which are older than the days given in config
  197. */
  198. cleanOldItems() {
  199. const votes = this.getVotes();
  200. const cleaned = votes.filter(item => !this.isOld(item));
  201. if (votes.length === cleaned.length)
  202. return;
  203. this.setVotes(cleaned);
  204. }
  205. /**
  206. * interval checks
  207. */
  208. cron() {
  209. this.cleanOldItems();
  210. this.check();
  211. }
  212. /**
  213. * sets an items status to claimed
  214. * @param item the item which should get claimed
  215. * @param uid the uid which claimed the item
  216. */
  217. flagItemClaimed(item, uid) {
  218. item.claimedBy = uid;
  219. item.claimedAt = Date.now();
  220. return this;
  221. }
  222. /**
  223. * creates a fully valid VoteItem
  224. * @param item the item which should be upgraded
  225. */
  226. createVoteItem(item) {
  227. return {
  228. ...item,
  229. added: Date.now(),
  230. hash: this.getHash(item),
  231. claimedBy: null,
  232. claimedAt: 0
  233. };
  234. }
  235. }
  236. class TeamSpeakServers extends Vote {
  237. constructor({ key, sid, createCommand }) {
  238. super();
  239. this.namespace = "teamspeakServersDotOrg_";
  240. this.apikey = key;
  241. this.sid = sid;
  242. this.registerCommand(createCommand);
  243. this.init();
  244. }
  245. registerCommand(createCommand) {
  246. createCommand("vote")
  247. .help("retrieves the vote link from teamspeak-servers.org")
  248. .manual("retrieves the vote link for teamspeak-servers.org")
  249. .manual(`vote daily to get rewarded with servergroups!`)
  250. .exec((client, _, reply) => {
  251. reply(`[b][url=https://teamspeak-servers.org/server/${this.sid}/vote/?username=${encodeURI(client.nick())}]VOTE HERE[/url]`);
  252. reply(`It can take a few minutes until your vote gets counted!`);
  253. if (removeAfter === -1) {
  254. reply(`You have have voted ${this.getVotesByClient(client).length} times!`);
  255. }
  256. else {
  257. reply(`You have have voted ${this.getVotesByClient(client).length} times in the last ${removeTime} days!`);
  258. }
  259. });
  260. }
  261. async check() {
  262. const votes = await this.fetchVotes();
  263. votes.forEach(vote => this.requestAdd({
  264. nickname: vote.nickname,
  265. timestamp: vote.timestamp
  266. }));
  267. }
  268. fetchVotes() {
  269. return new Promise((fulfill, reject) => {
  270. http.simpleRequest({
  271. method: "GET",
  272. url: `https://teamspeak-servers.org/api/?object=servers&element=votes&key=${this.apikey}&format=json`
  273. }, (err, res) => {
  274. if (err)
  275. return reject(new Error(`Failed to retrieve data from teamspeak-servers.org api! (Error ${err})`));
  276. if (res.statusCode !== 200)
  277. return reject(new Error(`Failed to retrieve data from teamspeak-servers.org api! (Code ${res.statusCode})`));
  278. try {
  279. fulfill(JSON.parse(res.data.toString()).votes);
  280. }
  281. catch (e) {
  282. engine.log(`got response from teamspeak-servers.org: ${res.data.toString()}`);
  283. return reject(e);
  284. }
  285. });
  286. });
  287. }
  288. }
  289. const votings = [];
  290. function doGlobalCheck() {
  291. votings.forEach(vote => vote.cron());
  292. backend.getClients()
  293. .filter(c => !c.isSelf())
  294. .forEach(c => votings.forEach(v => v.checkClient(c)));
  295. }
  296. event.on("connect", () => {
  297. if (initialized)
  298. return;
  299. initialized = true;
  300. doGlobalCheck();
  301. });
  302. event.on("disconnect", () => {
  303. initialized = false;
  304. });
  305. event.on("clientMove", ({ fromChannel, client }) => {
  306. if (fromChannel || client.isSelf())
  307. return;
  308. votings.forEach(v => v.checkClient(client));
  309. });
  310. event.on("clientNick", client => votings.forEach(v => v.checkClient(client)));
  311. event.on("serverGroupAdded", ev => votings.forEach(v => v.checkClient(ev.client)));
  312. event.on("serverGroupRemoved", ev => votings.forEach(v => v.checkClient(ev.client)));
  313. setInterval(() => {
  314. if (!backend.isConnected())
  315. return;
  316. votings.forEach(vote => vote.cron());
  317. }, CHECK_INTERVAL);
  318. event.on("load", () => {
  319. const command = require("command");
  320. const { createCommand } = command;
  321. votings.push(new TeamSpeakServers({ key, sid, createCommand }));
  322. if (backend.isConnected()) {
  323. initialized = true;
  324. doGlobalCheck();
  325. }
  326. });
  327. });
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement