<commit_before>'use strict';

exports.BattleMovedex = {
	allyswitch: {
		inherit: true,
		desc: "The user swaps positions with its ally on the opposite side of the field. Fails if there is no Pokemon at that position, if the user is the only Pokemon on its side, or if the user is in the middle.",
		shortDesc: "Switches position with the ally on the far side.",
		priority: 1,
	},
	bestow: {
		inherit: true,
		desc: "The target receives the user's held item. Fails if the user has no item or is holding a Mail, if the target is already holding an item, if the user is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, a Pokemon that can Mega Evolve holding the Mega Stone for its species, or if the target is one of those Pokemon and the user is holding the respective item.",
	},
	brickbreak: {
		inherit: true,
		desc: "If this attack does not miss, the effects of Reflect and Light Screen end for the target's side of the field before damage is calculated.",
	},
	covet: {
		inherit: true,
		desc: "If this attack was successful and the user has not fainted, it steals the target's held item if the user is not holding one. The target's item is not stolen if it is a Mail, or if the target is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, or a Pokemon that can Mega Evolve holding the Mega Stone for its species. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	darkvoid: {
		inherit: true,
		desc: "Causes the target to fall asleep.",
		shortDesc: "Puts the foe(s) to sleep.",
		accuracy: 80,
		onTryMove: function () {},
	},
	destinybond: {
		inherit: true,
		onPrepareHit: function (pokemon) {
			pokemon.removeVolatile('destinybond');
		},
	},
	diamondstorm: {
		inherit: true,
		desc: "Has a 50% chance to raise the user's Defense by 1 stage.",
		shortDesc: "50% chance to raise user's Def by 1 for each hit.",
		secondary: {
			chance: 50,
			self: {
				boosts: {
					def: 1,
				},
			},
		},
	},
	fellstinger: {
		inherit: true,
		basePower: 30,
		effect: {
			duration: 1,
			onAfterMoveSecondarySelf: function (pokemon, target, move) {
				if (!target || target.fainted || target.hp <= 0) this.boost({atk:2}, pokemon, pokemon, move);
				pokemon.removeVolatile('fellstinger');
			},
		},
	},
	flyingpress: {
		inherit: true,
		basePower: 80,
	},
	heavyslam: {
		inherit: true,
		desc: "The power of this move depends on (user's weight / target's weight), rounded down. Power is equal to 120 if the result is 5 or more, 100 if 4, 80 if 3, 60 if 2, and 40 if 1 or less.",
	},
	kingsshield: {
		inherit: true,
		desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 2 stages. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails or if the user's last move used is not Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard. Fails if the user moves last this turn.",
		shortDesc: "Protects from attacks. Contact try: lowers Atk by 2.",
		effect: {
			duration: 1,
			onStart: function (target) {
				this.add('-singleturn', target, 'Protect');
			},
			onTryHitPriority: 3,
			onTryHit: function (target, source, move) {
				if (!move.flags['protect'] || move.category === 'Status') return;
				this.add('-activate', target, 'move: Protect');
				let lockedmove = source.getVolatile('lockedmove');
				if (lockedmove) {
					// Outrage counter is reset
					if (source.volatiles['lockedmove'].duration === 2) {
						delete source.volatiles['lockedmove'];
					}
				}
				if (move.flags['contact']) {
					this.boost({atk:-2}, source, target, this.getMove("King's Shield"));
				}
				return null;
			},
		},
	},
	knockoff: {
		inherit: true,
		desc: "If the target is holding an item that can be removed from it, ignoring the Ability Sticky Hold, this move's power is multiplied by 1.5. If the user has not fainted, the target loses its held item. This move cannot cause Pokemon with the Ability Sticky Hold to lose their held item, cause Pokemon that can Mega Evolve to lose the Mega Stone for their species, or cause a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect to lose their Blue Orb, Red Orb, Griseous Orb, Plate, or Drive, respectively. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	leechlife: {
		inherit: true,
		basePower: 20,
		isViable: false,
		pp: 15,
	},
	minimize: {
		inherit: true,
		desc: "Raises the user's evasiveness by 2 stages. Whether or not the user's evasiveness was changed, Body Slam, Dragon Rush, Flying Press, Heat Crash, Phantom Force, Shadow Force, Steamroller, and Stomp will not check accuracy and have their damage doubled if used against the user while it is active.",
		effect: {
			noCopy: true,
			onSourceModifyDamage: function (damage, source, target, move) {
				if (move.id in {'stomp':1, 'steamroller':1, 'bodyslam':1, 'flyingpress':1, 'dragonrush':1, 'phantomforce':1, 'heatcrash':1, 'shadowforce':1}) {
					return this.chainModify(2);
				}
			},
			onAccuracy: function (accuracy, target, source, move) {
				if (move.id in {'stomp':1, 'steamroller':1, 'bodyslam':1, 'flyingpress':1, 'dragonrush':1, 'phantomforce':1, 'heatcrash':1, 'shadowforce':1}) {
					return true;
				}
				return accuracy;
			},
		},
	},
	mistyterrain: {
		inherit: true,
		desc: "For 5 turns, the terrain becomes Misty Terrain. During the effect, the power of Dragon-type attacks used against grounded Pokemon is multiplied by 0.5 and grounded Pokemon cannot be inflicted with a major status condition. Camouflage transforms the user into a Fairy type, Nature Power becomes Moonblast, and Secret Power has a 30% chance to lower Special Attack by 1 stage. Fails if the current terrain is Misty Terrain.",
		effect: {
			duration: 5,
			durationCallback: function (source, effect) {
				if (source && source.hasItem('terrainextender')) {
					return 8;
				}
				return 5;
			},
			onSetStatus: function (status, target, source, effect) {
				if (!target.isGrounded() || target.isSemiInvulnerable()) return;
				if (effect.id === 'synchronize' || (effect.effectType === 'Move' && !effect.secondaries)) {
					this.add('-activate', target, 'move: Misty Terrain');
				}
				return false;
			},
			onBasePower: function (basePower, attacker, defender, move) {
				if (move.type === 'Dragon' && defender.isGrounded() && !defender.isSemiInvulnerable()) {
					this.debug('misty terrain weaken');
					return this.chainModify(0.5);
				}
			},
			onStart: function (battle, source, effect) {
				if (effect && effect.effectType === 'Ability') {
					this.add('-fieldstart', 'move: Misty Terrain', '[from] ability: ' + effect, '[of] ' + source);
				} else {
					this.add('-fieldstart', 'move: Misty Terrain');
				}
			},
			onResidualOrder: 21,
			onResidualSubOrder: 2,
			onEnd: function (side) {
				this.add('-fieldend', 'Misty Terrain');
			},
		},
	},
	mysticalfire: {
		inherit: true,
		basePower: 65,
	},
	paraboliccharge: {
		inherit: true,
		basePower: 50,
	},
	rockblast: {
		inherit: true,
		flags: {protect: 1, mirror: 1},
	},
	powder: {
		inherit: true,
		effect: {
			duration: 1,
			onStart: function (target) {
				this.add('-singleturn', target, 'Powder');
			},
			onTryMovePriority: 1,
			onTryMove: function (pokemon, target, move) {
				if (move.type === 'Fire') {
					this.add('-activate', pokemon, 'move: Powder');
					this.damage(this.clampIntRange(Math.round(pokemon.maxhp / 4), 1));
					return false;
				}
			},
		},
	},
	reflecttype: {
		inherit: true,
		desc: "Causes the user's types to become the same as the current types of the target. Fails if the user is an Arceus.",
	},
	sheercold: {
		inherit: true,
		desc: "Deals damage to the target equal to the target's maximum HP. Ignores accuracy and evasiveness modifiers. This attack's accuracy is equal to (user's level - target's level + 30)%, and fails if the target is at a higher level. Pokemon with the Ability Sturdy are immune.",
		shortDesc: "OHKOs the target. Fails if user is a lower level.",
		ohko: true,
	},
	suckerpunch: {
		inherit: true,
		basePower: 80,
	},
	swagger: {
		inherit: true,
		accuracy: 90,
	},
	switcheroo: {
		inherit: true,
		desc: "The user swaps its held item with the target's held item. Fails if either the user or the target is holding a Mail, if neither is holding an item, if the user is trying to give or take a Mega Stone to or from the species that can Mega Evolve with it, or if the user is trying to give or take a Blue Orb, a Red Orb, a Griseous Orb, a Plate, or a Drive to or from a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect, respectively. Pokemon with the Ability Sticky Hold are immune.",
	},
	tackle: {
		inherit: true,
		basePower: 50,
	},
	thief: {
		inherit: true,
		desc: "If this attack was successful and the user has not fainted, it steals the target's held item if the user is not holding one. The target's item is not stolen if it is a Mail, or if the target is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, or a Pokemon that can Mega Evolve holding the Mega Stone for its species. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	thousandarrows: {
		inherit: true,
		isUnreleased: true,
	},
	thousandwaves: {
		inherit: true,
		isUnreleased: true,
	},
	thunderwave: {
		inherit: true,
		accuracy: 100,
	},
	trick: {
		inherit: true,
		desc: "The user swaps its held item with the target's held item. Fails if either the user or the target is holding a Mail, if neither is holding an item, if the user is trying to give or take a Mega Stone to or from the species that can Mega Evolve with it, or if the user is trying to give or take a Blue Orb, a Red Orb, a Griseous Orb, a Plate, or a Drive to or from a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect, respectively. Pokemon with the Ability Sticky Hold are immune.",
	},
	watershuriken: {
		inherit: true,
		category: "Physical",
		desc: "Hits two to five times. Has a 1/3 chance to hit two or three times, and a 1/6 chance to hit four or five times. If one of the hits breaks the target's substitute, it will take damage for the remaining hits. If the user has the Ability Skill Link, this move will always hit five times.",
	},
	wideguard: {
		inherit: true,
		desc: "The user and its party members are protected from damaging attacks made by other Pokemon, including allies, during this turn that target all adjacent foes or all adjacent Pokemon. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails or if the user's last move used is not Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
		shortDesc: "Protects allies from multi-target hits this turn.",
		effect: {
			duration: 1,
			onStart: function (target, source) {
				this.add('-singleturn', source, 'Wide Guard');
			},
			onTryHitPriority: 4,
			onTryHit: function (target, source, effect) {
				// Wide Guard blocks damaging spread moves
				if (effect && (effect.category === 'Status' || (effect.target !== 'allAdjacent' && effect.target !== 'allAdjacentFoes'))) {
					return;
				}
				this.add('-activate', target, 'move: Wide Guard');
				let lockedmove = source.getVolatile('lockedmove');
				if (lockedmove) {
					// Outrage counter is reset
					if (source.volatiles['lockedmove'].duration === 2) {
						delete source.volatiles['lockedmove'];
					}
				}
				return null;
			},
		},
	},
};
<commit_msg>Gen 6: Fix up a few descriptions (#3873)<commit_after>'use strict';

exports.BattleMovedex = {
	allyswitch: {
		inherit: true,
		desc: "The user swaps positions with its ally on the opposite side of the field. Fails if there is no Pokemon at that position, if the user is the only Pokemon on its side, or if the user is in the middle.",
		shortDesc: "Switches position with the ally on the far side.",
		priority: 1,
	},
	bestow: {
		inherit: true,
		desc: "The target receives the user's held item. Fails if the user has no item or is holding a Mail, if the target is already holding an item, if the user is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, a Pokemon that can Mega Evolve holding the Mega Stone for its species, or if the target is one of those Pokemon and the user is holding the respective item.",
	},
	brickbreak: {
		inherit: true,
		desc: "If this attack does not miss, the effects of Reflect and Light Screen end for the target's side of the field before damage is calculated.",
	},
	covet: {
		inherit: true,
		desc: "If this attack was successful and the user has not fainted, it steals the target's held item if the user is not holding one. The target's item is not stolen if it is a Mail, or if the target is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, or a Pokemon that can Mega Evolve holding the Mega Stone for its species. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	darkvoid: {
		inherit: true,
		desc: "Causes the target to fall asleep.",
		shortDesc: "Puts the foe(s) to sleep.",
		accuracy: 80,
		onTryMove: function () {},
	},
	destinybond: {
		inherit: true,
		onPrepareHit: function (pokemon) {
			pokemon.removeVolatile('destinybond');
		},
	},
	diamondstorm: {
		inherit: true,
		desc: "Has a 50% chance to raise the user's Defense by 1 stage.",
		shortDesc: "50% chance to raise user's Def by 1 for each hit.",
		secondary: {
			chance: 50,
			self: {
				boosts: {
					def: 1,
				},
			},
		},
	},
	fellstinger: {
		inherit: true,
		basePower: 30,
		effect: {
			duration: 1,
			onAfterMoveSecondarySelf: function (pokemon, target, move) {
				if (!target || target.fainted || target.hp <= 0) this.boost({atk:2}, pokemon, pokemon, move);
				pokemon.removeVolatile('fellstinger');
			},
		},
		desc: "Raises the user's Attack by 2 stages if this move knocks out the target.",
		shortDesc: "Raises user's Attack by 2 if this KOes the target.",
	},
	flyingpress: {
		inherit: true,
		basePower: 80,
	},
	heavyslam: {
		inherit: true,
		desc: "The power of this move depends on (user's weight / target's weight), rounded down. Power is equal to 120 if the result is 5 or more, 100 if 4, 80 if 3, 60 if 2, and 40 if 1 or less.",
	},
	kingsshield: {
		inherit: true,
		desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 2 stages. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails or if the user's last move used is not Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard. Fails if the user moves last this turn.",
		shortDesc: "Protects from attacks. Contact try: lowers Atk by 2.",
		effect: {
			duration: 1,
			onStart: function (target) {
				this.add('-singleturn', target, 'Protect');
			},
			onTryHitPriority: 3,
			onTryHit: function (target, source, move) {
				if (!move.flags['protect'] || move.category === 'Status') return;
				this.add('-activate', target, 'move: Protect');
				let lockedmove = source.getVolatile('lockedmove');
				if (lockedmove) {
					// Outrage counter is reset
					if (source.volatiles['lockedmove'].duration === 2) {
						delete source.volatiles['lockedmove'];
					}
				}
				if (move.flags['contact']) {
					this.boost({atk:-2}, source, target, this.getMove("King's Shield"));
				}
				return null;
			},
		},
	},
	knockoff: {
		inherit: true,
		desc: "If the target is holding an item that can be removed from it, ignoring the Ability Sticky Hold, this move's power is multiplied by 1.5. If the user has not fainted, the target loses its held item. This move cannot cause Pokemon with the Ability Sticky Hold to lose their held item, cause Pokemon that can Mega Evolve to lose the Mega Stone for their species, or cause a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect to lose their Blue Orb, Red Orb, Griseous Orb, Plate, or Drive, respectively. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	leechlife: {
		inherit: true,
		basePower: 20,
		isViable: false,
		pp: 15,
	},
	minimize: {
		inherit: true,
		desc: "Raises the user's evasiveness by 2 stages. Whether or not the user's evasiveness was changed, Body Slam, Dragon Rush, Flying Press, Heat Crash, Phantom Force, Shadow Force, Steamroller, and Stomp will not check accuracy and have their damage doubled if used against the user while it is active.",
		effect: {
			noCopy: true,
			onSourceModifyDamage: function (damage, source, target, move) {
				if (move.id in {'stomp':1, 'steamroller':1, 'bodyslam':1, 'flyingpress':1, 'dragonrush':1, 'phantomforce':1, 'heatcrash':1, 'shadowforce':1}) {
					return this.chainModify(2);
				}
			},
			onAccuracy: function (accuracy, target, source, move) {
				if (move.id in {'stomp':1, 'steamroller':1, 'bodyslam':1, 'flyingpress':1, 'dragonrush':1, 'phantomforce':1, 'heatcrash':1, 'shadowforce':1}) {
					return true;
				}
				return accuracy;
			},
		},
	},
	mistyterrain: {
		inherit: true,
		desc: "For 5 turns, the terrain becomes Misty Terrain. During the effect, the power of Dragon-type attacks used against grounded Pokemon is multiplied by 0.5 and grounded Pokemon cannot be inflicted with a major status condition. Camouflage transforms the user into a Fairy type, Nature Power becomes Moonblast, and Secret Power has a 30% chance to lower Special Attack by 1 stage. Fails if the current terrain is Misty Terrain.",
		effect: {
			duration: 5,
			durationCallback: function (source, effect) {
				if (source && source.hasItem('terrainextender')) {
					return 8;
				}
				return 5;
			},
			onSetStatus: function (status, target, source, effect) {
				if (!target.isGrounded() || target.isSemiInvulnerable()) return;
				if (effect.id === 'synchronize' || (effect.effectType === 'Move' && !effect.secondaries)) {
					this.add('-activate', target, 'move: Misty Terrain');
				}
				return false;
			},
			onBasePower: function (basePower, attacker, defender, move) {
				if (move.type === 'Dragon' && defender.isGrounded() && !defender.isSemiInvulnerable()) {
					this.debug('misty terrain weaken');
					return this.chainModify(0.5);
				}
			},
			onStart: function (battle, source, effect) {
				if (effect && effect.effectType === 'Ability') {
					this.add('-fieldstart', 'move: Misty Terrain', '[from] ability: ' + effect, '[of] ' + source);
				} else {
					this.add('-fieldstart', 'move: Misty Terrain');
				}
			},
			onResidualOrder: 21,
			onResidualSubOrder: 2,
			onEnd: function (side) {
				this.add('-fieldend', 'Misty Terrain');
			},
		},
	},
	mysticalfire: {
		inherit: true,
		basePower: 65,
	},
	paraboliccharge: {
		inherit: true,
		basePower: 50,
	},
	rockblast: {
		inherit: true,
		flags: {protect: 1, mirror: 1},
	},
	powder: {
		inherit: true,
		effect: {
			duration: 1,
			onStart: function (target) {
				this.add('-singleturn', target, 'Powder');
			},
			onTryMovePriority: 1,
			onTryMove: function (pokemon, target, move) {
				if (move.type === 'Fire') {
					this.add('-activate', pokemon, 'move: Powder');
					this.damage(this.clampIntRange(Math.round(pokemon.maxhp / 4), 1));
					return false;
				}
			},
		},
	},
	reflecttype: {
		inherit: true,
		desc: "Causes the user's types to become the same as the current types of the target. Fails if the user is an Arceus.",
	},
	sheercold: {
		inherit: true,
		desc: "Deals damage to the target equal to the target's maximum HP. Ignores accuracy and evasiveness modifiers. This attack's accuracy is equal to (user's level - target's level + 30)%, and fails if the target is at a higher level. Pokemon with the Ability Sturdy are immune.",
		shortDesc: "OHKOs the target. Fails if user is a lower level.",
		ohko: true,
	},
	suckerpunch: {
		inherit: true,
		basePower: 80,
	},
	swagger: {
		inherit: true,
		accuracy: 90,
	},
	switcheroo: {
		inherit: true,
		desc: "The user swaps its held item with the target's held item. Fails if either the user or the target is holding a Mail, if neither is holding an item, if the user is trying to give or take a Mega Stone to or from the species that can Mega Evolve with it, or if the user is trying to give or take a Blue Orb, a Red Orb, a Griseous Orb, a Plate, or a Drive to or from a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect, respectively. Pokemon with the Ability Sticky Hold are immune.",
	},
	tackle: {
		inherit: true,
		basePower: 50,
	},
	thief: {
		inherit: true,
		desc: "If this attack was successful and the user has not fainted, it steals the target's held item if the user is not holding one. The target's item is not stolen if it is a Mail, or if the target is a Kyogre holding a Blue Orb, a Groudon holding a Red Orb, a Giratina holding a Griseous Orb, an Arceus holding a Plate, a Genesect holding a Drive, or a Pokemon that can Mega Evolve holding the Mega Stone for its species. Items lost to this move cannot be regained with Recycle or the Ability Harvest.",
	},
	thousandarrows: {
		inherit: true,
		isUnreleased: true,
	},
	thousandwaves: {
		inherit: true,
		isUnreleased: true,
	},
	thunderwave: {
		inherit: true,
		accuracy: 100,
	},
	trick: {
		inherit: true,
		desc: "The user swaps its held item with the target's held item. Fails if either the user or the target is holding a Mail, if neither is holding an item, if the user is trying to give or take a Mega Stone to or from the species that can Mega Evolve with it, or if the user is trying to give or take a Blue Orb, a Red Orb, a Griseous Orb, a Plate, or a Drive to or from a Kyogre, a Groudon, a Giratina, an Arceus, or a Genesect, respectively. Pokemon with the Ability Sticky Hold are immune.",
	},
	watershuriken: {
		inherit: true,
		category: "Physical",
		desc: "Hits two to five times. Has a 1/3 chance to hit two or three times, and a 1/6 chance to hit four or five times. If one of the hits breaks the target's substitute, it will take damage for the remaining hits. If the user has the Ability Skill Link, this move will always hit five times.",
	},
	wideguard: {
		inherit: true,
		desc: "The user and its party members are protected from damaging attacks made by other Pokemon, including allies, during this turn that target all adjacent foes or all adjacent Pokemon. This move modifies the same 1/X chance of being successful used by other protection moves, where X starts at 1 and triples each time this move is successfully used, but does not use the chance to check for failure. X resets to 1 if this move fails or if the user's last move used is not Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard. Fails if the user moves last this turn or if this move is already in effect for the user's side.",
		shortDesc: "Protects allies from multi-target hits this turn.",
		effect: {
			duration: 1,
			onStart: function (target, source) {
				this.add('-singleturn', source, 'Wide Guard');
			},
			onTryHitPriority: 4,
			onTryHit: function (target, source, effect) {
				// Wide Guard blocks damaging spread moves
				if (effect && (effect.category === 'Status' || (effect.target !== 'allAdjacent' && effect.target !== 'allAdjacentFoes'))) {
					return;
				}
				this.add('-activate', target, 'move: Wide Guard');
				let lockedmove = source.getVolatile('lockedmove');
				if (lockedmove) {
					// Outrage counter is reset
					if (source.volatiles['lockedmove'].duration === 2) {
						delete source.volatiles['lockedmove'];
					}
				}
				return null;
			},
		},
	},
};
<|endoftext|>


<commit_before>const functions = require("firebase-functions")
const { Nuxt } = require("nuxt")
const express = require("express")
const { SitemapStream, streamToPromise } = require("sitemap")
const app = express()

let sitemap
app.get("/sitemap.xml", (req, res) => {
  res.header("Content-Type", "application/xml")
  if (sitemap) {
    res.send(sitemap)
    return
  }

  try {
    const smStream = new SitemapStream({
      hostname: "https://shikibetsuhyo.yysaki.com",
    })

    const urls = ["shiren", "shiren2", "shiren3", "shiren4", "shiren5"]
      .map((name) => `/${name}/`)
      .concat(["/"])
    urls.forEach((url) => {
      smStream.write({ url, changeFreq: "monthly" })
    })
    smStream.end()

    streamToPromise(smStream)
      .then((sm) => (sitemap = sm))
      .catch((e) => {
        throw e
      })
    smStream.pipe(res).on("error", (e) => {
      throw e
    })
  } catch (e) {
    console.error(e)
    res.status(500).end()
  }
})

const nuxt = new Nuxt({ buildDir: "nuxt", dev: false })

function handleRequest(req, res) {
  res.set("Cache-Control", "public, max-age=300, s-maxage=600")
  return nuxt.render(req, res)
}

app.use(handleRequest)
exports.ssr = functions.https.onRequest(app)
<commit_msg>Disable caching<commit_after>const functions = require("firebase-functions")
const { Nuxt } = require("nuxt")
const express = require("express")
const { SitemapStream, streamToPromise } = require("sitemap")
const app = express()

let sitemap
app.get("/sitemap.xml", (req, res) => {
  res.header("Content-Type", "application/xml")
  if (sitemap) {
    res.send(sitemap)
    return
  }

  try {
    const smStream = new SitemapStream({
      hostname: "https://shikibetsuhyo.yysaki.com",
    })

    const urls = ["shiren", "shiren2", "shiren3", "shiren4", "shiren5"]
      .map((name) => `/${name}/`)
      .concat(["/"])
    urls.forEach((url) => {
      smStream.write({ url, changeFreq: "monthly" })
    })
    smStream.end()

    streamToPromise(smStream)
      .then((sm) => (sitemap = sm))
      .catch((e) => {
        throw e
      })
    smStream.pipe(res).on("error", (e) => {
      throw e
    })
  } catch (e) {
    console.error(e)
    res.status(500).end()
  }
})

const nuxt = new Nuxt({ buildDir: "nuxt", dev: false })
app.use(nuxt.render)

exports.ssr = functions.https.onRequest(app)
<|endoftext|>


<commit_before>/**
 * ResetToken
 *
 * @module      :: Model
 * @description :: Describes a users reset token
 * @docs        :: http://waterlock.ninja/documentation
 */

module.exports = {

  attributes: require('waterlock').models.resetToken.attributes({

    /* e.g.
    nickname: 'string'
    */

  }),

  beforeCreate: require('waterlock').models.resetToken.beforeCreate,
  afterCreate: require('waterlock').models.resetToken.afterCreate
};
<commit_msg>Update ResetToken.js<commit_after>'use strict';
/**
 * ResetToken
 *
 * @module      :: Model
 * @description :: Describes a users reset token
 * @docs        :: http://waterlock.ninja/documentation
 */

module.exports = {

  attributes: require('waterlock').models.resetToken.attributes({

    /* e.g.
    nickname: 'string'
    */

  }),

  beforeCreate: require('waterlock').models.resetToken.beforeCreate,
  afterCreate: require('waterlock').models.resetToken.afterCreate
};
<|endoftext|>


<commit_before>// Setting variables

console.log('Hello World! I\'m starting!')

const botConfig = require('./botconfig.json')
const Discord = require('discord.js')
const Wikia = require('node-wikia')
const Database = require('better-sqlite3')

const bot = new Discord.Client()
const fcpattern = new RegExp(/^(\d{4}-\d{4}-\d{4})$/g)

var wikia = new Wikia('animalcrossing')
var db = new Database('userInfo.db')

// Launching bot
bot.on('ready', async () => {
  console.log(`${bot.user.username} is ready!`)

  try {
    let link = await bot.generateInvite(['READ_MESSAGES', 'SEND_MESSAGES'])
    console.log(link)
  } catch (e) {
    console.log(e.stack)
  }
})

// on message

bot.on('message', async message => {
  // bot message or not start with prefix = return
  if (message.author.bot) return
  if (!message.content.startsWith(botConfig.prefix)) return

  let user = message.author
  let messageArray = message.content.trim().split(' ')
  let command = messageArray[0].replace(botConfig.prefix, '')
  let args = messageArray.slice(1)
  // Commands code starts here!
  switch (command.toUpperCase()) {
    // !userinfo
    case 'USERINFO':
      let userinfo = new Discord.RichEmbed()
        .setColor('RANDOM')
        .setAuthor(user.username)
        .setDescription(`#${user.discriminator}`)
        .addField('Created on: ', user.createdAt)
        .setThumbnail(user.avatarURL)
      message.channel.send({ embed: userinfo })
      break
    // !wiki
    case 'WIKI':
      if (args.length > 0) {
        wikia.getSearchList({'query': args.join(' ')}).then(function (data) {
          message.channel.send(data.items[0].url)
        })
        .fail(function (e) {
          message.channel.send('❌ There isn\'t any wiki page about `' + args + '`')
          console.log(e.stack)
        })
      } else {
        message.channel.send(botConfig.wiki)
      }
      break

    // !help
    // DONT FORGET TO ADD NEW COMMANDS HERE!

    case 'HELP':
      let help = new Discord.RichEmbed()
        .setColor('RANDOM')
        .setTitle('⁉ Commands available')
        .addField('!help', 'Get a list of commands')
        .addField('!wiki [search]', 'Search the Wiki')
        .addField('!info [mention]', 'Get user info')
        .addField('!fc [code]', 'Add your Friend Code')
        .addField('!fc [mention]', 'Get someone elses Friend Code')
        .addField('!name [name]', 'Add your Name')
        .addField('!name [mention]', 'Get someone elses Name')
        .addField('!town [town]', 'Add your Town')
        .addField('!town [mention]', 'Get someone elses Town')
        .addField('!fruit [fruit]', 'Add your Native Fruit')
        .addField('!fruit [mention]', 'Get someone elses Native Fruit')
        .addField('!note [note]', 'Add your Note')
        .addField('!note [mention]', 'Get someone elses Note')
        .addField('Author', 'This bot is made by <@237985610084777994> with help from <@189769721653100546> and GitHub Contributors!')
        .setThumbnail(message.guild.iconURL)
      message.channel.send({ embed: help })
      break

    // !fc

    case 'FC':
      if (args.length === 0) {
        var usr = user
        var usrinfo = getUserInfo(usr.id)
        if (usrinfo.FriendCode != null) {
          message.channel.send(usr.username + "'s Friend Code is: `" + usrinfo.FriendCode + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet!')
        }
      } else if (args.length === 1) {
        if (fcpattern.test(args[0])) {
          setUserInfo(user.id, { FriendCode: args[0] })
          message.channel.send('✅ Your Friend Code is now `' + args[0] + '`')
        } else if (message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.FriendCode != null) {
            message.channel.send(usr.username + "'s Friend Code is: `" + usrinfo.FriendCode + '`')
          } else {
            message.channel.send('❌' + usr.username + ' has not set a Friend Code yet')
          }
        } else {
          message.channel.send('❌ Invalid Friend Code or User!')
          message.channel.send('The code format should be `xxxx-xxxx-xxxx`')
        }
      } else {
        message.channel.send('Usage: `!fc [code]` or `!fc [mention]`')
      }
      break

    // !name

    case 'NAME':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Name != null) {
          message.channel.send(usr.username + "'s Name is: `" + usrinfo.Name + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Name yet')
        }
      } else if (args.length === 1 && message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Name != null) {
            message.channel.send(usr.username + "'s Name is: `" + usrinfo.Name + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Name yet')
          }
      } else if (args.join(" ").length <= 8 && message.mentions.users.first() == null) {
        setUserInfo(user.id, { Name: args[0].join(" ") })
        message.channel.send('✅ Your Name is now `' + args[0].join(" ") + '`')
      } else {
        message.channel.send('Usage: `!name [name]` or `!name [mention]`')
      }
      break

    // !town

    case 'TOWN':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Town != null) {
          message.channel.send(usr.username + "'s Town is: `" + usrinfo.Town + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Town Name yet!')
        }
      } else if (args.length === 1 && message.mentions.users.first() != null) {
        usr = message.mentions.users.first()
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Town != null) {
          message.channel.send(usr.username + "'s Town is: `" + usrinfo.Town + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Town Name yet')
        }
      } else if (args.join(" ").length <= 8 && message.mentions.users.first() == null) {
        setUserInfo(user.id, { Town: args.join(" ") })
        message.channel.send('✅ Your Town is now `' + args.join(" ") + '`')
      } else {
        message.channel.send('Usage: `!town [town]` or `!town [mention]`')
      }
      break

    // !fruit

    case 'FRUIT':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Fruit != null) {
          message.channel.send(usr.username + "'s Fruit is: `" + usrinfo.Fruit + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Fruit yet')
        }
      } else if (args.length === 1) {
        if (message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Fruit != null) {
            message.channel.send(usr.username + "'s Fruit is: `" + usrinfo.Fruit + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Fruit yet')
          }
        } else {
          switch (args[0].toUpperCase()) {
            case 'ALL':
            case 'FULL':
              setUserInfo(user.id, { Fruit: 'All' })
              message.channel.send('☑ Your Fruit is now `All`')
              break
            case 'PEACH':
            case 'PEACHES':
              setUserInfo(user.id, { Fruit: 'Peach' })
              message.channel.send('🍑 Your Fruit is now `Peach`')
              break
            case 'PEAR':
            case 'PEARS':
              setUserInfo(user.id, { Fruit: 'Pear' })
              message.channel.send('🍐 Your Fruit is now `Pear`')
              break
            case 'APPLE':
            case 'APPLES':
              setUserInfo(user.id, { Fruit: 'Apple' })
              message.channel.send('🍎 Your Fruit is now `Apple`')
              break
            case 'ORANGE':
            case 'ORANGES':
              setUserInfo(user.id, { Fruit: 'Orange' })
              message.channel.send('🍊 Your Fruit is now `Orange`')
              break
            case 'CHERRY':
            case 'CHERRIES':
              setUserInfo(user.id, { Fruit: 'Cherry' })
              message.channel.send('🍒 Your Fruit is now `Cherry`')
              break
            default:
              message.channel.send('❌ Invalid Fruit!')
              break
          }
        }
      } else {
        message.channel.send('Usage: `!fruit [fruit]` or `!fruit [mention]`')
      }
      break

    // !note

    case 'NOTE':
      try {
        if (args.length === 0) {
          usr = user
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Note != null) {
            message.channel.send(usr.username + "'s Note is: `" + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
          }
        } else if (message.mentions.users.first() == null && args.length > 0) {
          setUserInfo(user.id, { Note: args.join(' ') })
          message.channel.send('✅ Your Note is now `' + args.join(' ') + '`')
        } else if (message.mentions.users.first() != null && args.length === 1) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Note != null) {
            message.channel.send(usr.username + "'s Note is: `" + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
          }
        } else {
          message.channel.send('Usage: `!note [note]` or `!note [mention]`')
        }
      } catch (e) {
          message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
      }
      break

    /* !info
    Lazy Programming. Just use try and catch */

    case 'INFO':
      try {
        if (args.length === 1) {
          if (message.mentions.users.first() != null) {
            usr = message.mentions.users.first()
            usrinfo = getUserInfo(usr.id)
            console.log(usrinfo)
            if (usrinfo.FriendCode != null) {
              message.channel.send(usr.username + "'s info is: \n Friend Code: `" + usrinfo.FriendCode + '` \n Name: `' + usrinfo.Name + '` \n Town: `' + usrinfo.Town + '` \n Fruit: `' + usrinfo.Fruit + '` \n Note: `' + usrinfo.Note + '`')
            } else {
              message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet')
            }
          } else {
            message.channel.send('Usage: `!info [mention]`')
          }
        } else if (args.length === 0) {
          usr = user
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.FriendCode != null) {
            message.channel.send(usr.username + "'s info is: \n Friend Code: `" + usrinfo.FriendCode + '` \n Name: `' + usrinfo.Name + '` \n Town: `' + usrinfo.Town + '` \n Fruit: `' + usrinfo.Fruit + '` \n Note: `' + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet')
          }
        } else {
          message.channel.send('Usage: `!info [mention]`')
        }
      } catch (e) {
        message.channel.send('❌ The user hasn\'t given any information to the bot!')
      }
      break

      /* !eval
      OI BE CAREFUL HERE! ONLY ALLOW SQUARE PEAR AND ANGELOANAN TO DO !EVAL OR THE SERVER MIGHT BE BROKEN! */

    case 'EVAL':
        // Check if user is squarepear or angeloanan
      if (message.author.id === '189769721653100546' || '237985610084777994') { // First is angeloanan second is squarepear
        console.log('Someone has just ran an eval command!')
          /* Set these things
            input = command input
            output = command output
          */

        let input = args
        // Catch error
        try { var output = eval(input) } catch (e) {  // Standard JS doesn't approve this line because of eval()
          let evalmsg = new Discord.RichEmbed()
          .setColor('RED')
          .setTitle('Eval Error')
          .setDescription('This code returns with an error!')
          .addField('Input Code', '`' + input + '`')
          .addField('Error', '`' + e + '`')
          message.channel.send({ embed: evalmsg })
          console.log('The eval returned with an error!')
        }
        // If there isn't any error
        let evalmsg = new Discord.RichEmbed()
        .setColor('GREEN')
        .setTitle('Eval Success')
        .setDescription('This code ran successfully!')
        .addField('Input Code', '`' + input + '`')
        .addField('Return', '`' + output + '`')
        .setFooter('This is an eval')
        message.channel.send({ embed: evalmsg })
        console.log('The eval returned with a success!')
      }
      break

    // if prefix + not valid command
    default:
      message.channel.send('❌ The command is invalid! Do `!help` if you need help.')
      break
  }
})

// PREMADE FUNCTIONS!

function getUserInfo (userID) {
  return db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)
}

function setUserInfo (userID, info) {
  var row = db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)

  if (row == null) {
    db.prepare(`INSERT INTO USERINFO (UserID) VALUES (?)`).run(userID)
    row = db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)
  }
  if (info.FriendCode != null) { row.FriendCode = info.FriendCode.trim() }
  if (info.Name != null) { row.Name = info.Name.trim() }
  if (info.Town != null) { row.Town = info.Town.trim() }
  if (info.Fruit != null) { row.Fruit = info.Fruit.trim() }
  if (info.Note != null) { row.Note = info.Note.trim() }

  db.prepare(`UPDATE USERINFO SET FriendCode = @FriendCode, Name = @Name, Town = @Town, Fruit = @Fruit, Note = @Note WHERE UserID=?`).run(userID, row)
}

bot.login(botConfig.token)
<commit_msg>Fixed max length of name and town to 8<commit_after>// Setting variables

console.log('Hello World! I\'m starting!')

const botConfig = require('./botconfig.json')
const Discord = require('discord.js')
const Wikia = require('node-wikia')
const Database = require('better-sqlite3')

const bot = new Discord.Client()
const fcpattern = new RegExp(/^(\d{4}-\d{4}-\d{4})$/g)

var wikia = new Wikia('animalcrossing')
var db = new Database('userInfo.db')

// Launching bot
bot.on('ready', async () => {
  console.log(`${bot.user.username} is ready!`)

  try {
    let link = await bot.generateInvite(['READ_MESSAGES', 'SEND_MESSAGES'])
    console.log(link)
  } catch (e) {
    console.log(e.stack)
  }
})

// on message

bot.on('message', async message => {
  // bot message or not start with prefix = return
  if (message.author.bot) return
  if (!message.content.startsWith(botConfig.prefix)) return

  let user = message.author
  let messageArray = message.content.trim().split(' ')
  let command = messageArray[0].replace(botConfig.prefix, '')
  let args = messageArray.slice(1)
  // Commands code starts here!
  switch (command.toUpperCase()) {
    // !userinfo
    case 'USERINFO':
      let userinfo = new Discord.RichEmbed()
        .setColor('RANDOM')
        .setAuthor(user.username)
        .setDescription(`#${user.discriminator}`)
        .addField('Created on: ', user.createdAt)
        .setThumbnail(user.avatarURL)
      message.channel.send({ embed: userinfo })
      break
    // !wiki
    case 'WIKI':
      if (args.length > 0) {
        wikia.getSearchList({'query': args.join(' ')}).then(function (data) {
          message.channel.send(data.items[0].url)
        })
        .fail(function (e) {
          message.channel.send('❌ There isn\'t any wiki page about `' + args + '`')
          console.log(e.stack)
        })
      } else {
        message.channel.send(botConfig.wiki)
      }
      break

    // !help
    // DONT FORGET TO ADD NEW COMMANDS HERE!

    case 'HELP':
      let help = new Discord.RichEmbed()
        .setColor('RANDOM')
        .setTitle('⁉ Commands available')
        .addField('!help', 'Get a list of commands')
        .addField('!wiki [search]', 'Search the Wiki')
        .addField('!info [mention]', 'Get user info')
        .addField('!fc [code]', 'Add your Friend Code')
        .addField('!fc [mention]', 'Get someone elses Friend Code')
        .addField('!name [name]', 'Add your Name')
        .addField('!name [mention]', 'Get someone elses Name')
        .addField('!town [town]', 'Add your Town')
        .addField('!town [mention]', 'Get someone elses Town')
        .addField('!fruit [fruit]', 'Add your Native Fruit')
        .addField('!fruit [mention]', 'Get someone elses Native Fruit')
        .addField('!note [note]', 'Add your Note')
        .addField('!note [mention]', 'Get someone elses Note')
        .addField('Author', 'This bot is made by <@237985610084777994> with help from <@189769721653100546> and GitHub Contributors!')
        .setThumbnail(message.guild.iconURL)
      message.channel.send({ embed: help })
      break

    // !fc

    case 'FC':
      if (args.length === 0) {
        var usr = user
        var usrinfo = getUserInfo(usr.id)
        if (usrinfo.FriendCode != null) {
          message.channel.send(usr.username + "'s Friend Code is: `" + usrinfo.FriendCode + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet!')
        }
      } else if (args.length === 1) {
        if (fcpattern.test(args[0])) {
          setUserInfo(user.id, { FriendCode: args[0] })
          message.channel.send('✅ Your Friend Code is now `' + args[0] + '`')
        } else if (message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.FriendCode != null) {
            message.channel.send(usr.username + "'s Friend Code is: `" + usrinfo.FriendCode + '`')
          } else {
            message.channel.send('❌' + usr.username + ' has not set a Friend Code yet')
          }
        } else {
          message.channel.send('❌ Invalid Friend Code or User!')
          message.channel.send('The code format should be `xxxx-xxxx-xxxx`')
        }
      } else {
        message.channel.send('Usage: `!fc [code]` or `!fc [mention]`')
      }
      break

    // !name

    case 'NAME':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Name != null) {
          message.channel.send(usr.username + "'s Name is: `" + usrinfo.Name + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Name yet')
        }
      } else if (args.length === 1 && message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Name != null) {
            message.channel.send(usr.username + "'s Name is: `" + usrinfo.Name + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Name yet')
          }
      } else if (args.join(" ").length <= 8 && message.mentions.users.first() == null) {
        setUserInfo(user.id, { Name: args.join(" ") })
        message.channel.send('✅ Your Name is now `' + args.join(" ") + '`')
      } else {
        message.channel.send('Usage: `!name [name]` or `!name [mention]`')
      }
      break

    // !town

    case 'TOWN':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Town != null) {
          message.channel.send(usr.username + "'s Town is: `" + usrinfo.Town + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Town Name yet!')
        }
      } else if (args.length === 1 && message.mentions.users.first() != null) {
        usr = message.mentions.users.first()
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Town != null) {
          message.channel.send(usr.username + "'s Town is: `" + usrinfo.Town + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Town Name yet')
        }
      } else if (args.join(" ").length <= 8 && message.mentions.users.first() == null) {
        setUserInfo(user.id, { Town: args.join(" ") })
        message.channel.send('✅ Your Town is now `' + args.join(" ") + '`')
      } else {
        message.channel.send('Usage: `!town [town]` or `!town [mention]`')
      }
      break

    // !fruit

    case 'FRUIT':
      if (args.length === 0) {
        usr = user
        usrinfo = getUserInfo(usr.id)
        if (usrinfo.Fruit != null) {
          message.channel.send(usr.username + "'s Fruit is: `" + usrinfo.Fruit + '`')
        } else {
          message.channel.send('❌ ' + usr.username + ' has not set a Fruit yet')
        }
      } else if (args.length === 1) {
        if (message.mentions.users.first() != null) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Fruit != null) {
            message.channel.send(usr.username + "'s Fruit is: `" + usrinfo.Fruit + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Fruit yet')
          }
        } else {
          switch (args[0].toUpperCase()) {
            case 'ALL':
            case 'FULL':
              setUserInfo(user.id, { Fruit: 'All' })
              message.channel.send('☑ Your Fruit is now `All`')
              break
            case 'PEACH':
            case 'PEACHES':
              setUserInfo(user.id, { Fruit: 'Peach' })
              message.channel.send('🍑 Your Fruit is now `Peach`')
              break
            case 'PEAR':
            case 'PEARS':
              setUserInfo(user.id, { Fruit: 'Pear' })
              message.channel.send('🍐 Your Fruit is now `Pear`')
              break
            case 'APPLE':
            case 'APPLES':
              setUserInfo(user.id, { Fruit: 'Apple' })
              message.channel.send('🍎 Your Fruit is now `Apple`')
              break
            case 'ORANGE':
            case 'ORANGES':
              setUserInfo(user.id, { Fruit: 'Orange' })
              message.channel.send('🍊 Your Fruit is now `Orange`')
              break
            case 'CHERRY':
            case 'CHERRIES':
              setUserInfo(user.id, { Fruit: 'Cherry' })
              message.channel.send('🍒 Your Fruit is now `Cherry`')
              break
            default:
              message.channel.send('❌ Invalid Fruit!')
              break
          }
        }
      } else {
        message.channel.send('Usage: `!fruit [fruit]` or `!fruit [mention]`')
      }
      break

    // !note

    case 'NOTE':
      try {
        if (args.length === 0) {
          usr = user
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Note != null) {
            message.channel.send(usr.username + "'s Note is: `" + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
          }
        } else if (message.mentions.users.first() == null && args.length > 0) {
          setUserInfo(user.id, { Note: args.join(' ') })
          message.channel.send('✅ Your Note is now `' + args.join(' ') + '`')
        } else if (message.mentions.users.first() != null && args.length === 1) {
          usr = message.mentions.users.first()
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.Note != null) {
            message.channel.send(usr.username + "'s Note is: `" + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
          }
        } else {
          message.channel.send('Usage: `!note [note]` or `!note [mention]`')
        }
      } catch (e) {
          message.channel.send('❌ ' + usr.username + ' has not set a Note yet')
      }
      break

    /* !info
    Lazy Programming. Just use try and catch */

    case 'INFO':
      try {
        if (args.length === 1) {
          if (message.mentions.users.first() != null) {
            usr = message.mentions.users.first()
            usrinfo = getUserInfo(usr.id)
            console.log(usrinfo)
            if (usrinfo.FriendCode != null) {
              message.channel.send(usr.username + "'s info is: \n Friend Code: `" + usrinfo.FriendCode + '` \n Name: `' + usrinfo.Name + '` \n Town: `' + usrinfo.Town + '` \n Fruit: `' + usrinfo.Fruit + '` \n Note: `' + usrinfo.Note + '`')
            } else {
              message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet')
            }
          } else {
            message.channel.send('Usage: `!info [mention]`')
          }
        } else if (args.length === 0) {
          usr = user
          usrinfo = getUserInfo(usr.id)
          if (usrinfo.FriendCode != null) {
            message.channel.send(usr.username + "'s info is: \n Friend Code: `" + usrinfo.FriendCode + '` \n Name: `' + usrinfo.Name + '` \n Town: `' + usrinfo.Town + '` \n Fruit: `' + usrinfo.Fruit + '` \n Note: `' + usrinfo.Note + '`')
          } else {
            message.channel.send('❌ ' + usr.username + ' has not set a Friend Code yet')
          }
        } else {
          message.channel.send('Usage: `!info [mention]`')
        }
      } catch (e) {
        message.channel.send('❌ The user hasn\'t given any information to the bot!')
      }
      break

      /* !eval
      OI BE CAREFUL HERE! ONLY ALLOW SQUARE PEAR AND ANGELOANAN TO DO !EVAL OR THE SERVER MIGHT BE BROKEN! */

    case 'EVAL':
        // Check if user is squarepear or angeloanan
      if (message.author.id === '189769721653100546' || '237985610084777994') { // First is angeloanan second is squarepear
        console.log('Someone has just ran an eval command!')
          /* Set these things
            input = command input
            output = command output
          */

        let input = args
        // Catch error
        try { var output = eval(input) } catch (e) {  // Standard JS doesn't approve this line because of eval()
          let evalmsg = new Discord.RichEmbed()
          .setColor('RED')
          .setTitle('Eval Error')
          .setDescription('This code returns with an error!')
          .addField('Input Code', '`' + input + '`')
          .addField('Error', '`' + e + '`')
          message.channel.send({ embed: evalmsg })
          console.log('The eval returned with an error!')
        }
        // If there isn't any error
        let evalmsg = new Discord.RichEmbed()
        .setColor('GREEN')
        .setTitle('Eval Success')
        .setDescription('This code ran successfully!')
        .addField('Input Code', '`' + input + '`')
        .addField('Return', '`' + output + '`')
        .setFooter('This is an eval')
        message.channel.send({ embed: evalmsg })
        console.log('The eval returned with a success!')
      }
      break

    // if prefix + not valid command
    default:
      message.channel.send('❌ The command is invalid! Do `!help` if you need help.')
      break
  }
})

// PREMADE FUNCTIONS!

function getUserInfo (userID) {
  return db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)
}

function setUserInfo (userID, info) {
  var row = db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)

  if (row == null) {
    db.prepare(`INSERT INTO USERINFO (UserID) VALUES (?)`).run(userID)
    row = db.prepare(`SELECT * FROM USERINFO WHERE UserID=?`).get(userID)
  }
  if (info.FriendCode != null) { row.FriendCode = info.FriendCode.trim() }
  if (info.Name != null) { row.Name = info.Name.trim() }
  if (info.Town != null) { row.Town = info.Town.trim() }
  if (info.Fruit != null) { row.Fruit = info.Fruit.trim() }
  if (info.Note != null) { row.Note = info.Note.trim() }

  db.prepare(`UPDATE USERINFO SET FriendCode = @FriendCode, Name = @Name, Town = @Town, Fruit = @Fruit, Note = @Note WHERE UserID=?`).run(userID, row)
}

bot.login(botConfig.token)
<|endoftext|>


<commit_before>/*
Copyright (c) 2011, Rob Ellis, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var TfIdf = require('lib/natural/tfidf/tfidf');
var tfidf;

describe('tfidf', function() {
    describe('stateless operations', function() {
        it('should tf', function() {
            expect(TfIdf.tf('document', { document : 2, one : 1 })).toBe(2);
            expect(TfIdf.tf('document', { greetings : 1, program : 1 })).toBe(0);
            expect(TfIdf.tf('program', { greetings : 1, program : 1 })).toBe(1);
        });
    });

    describe('keys', function() {
        it('should store and recall keys', function() {
            tfidf = new TfIdf();
            tfidf.addDocument('document one', 'un');
        	tfidf.addDocument('document Two', 'deux');

            tfidf.tfidfs('two', function(i, tfidf, key) {
                if(i == 0)
                    expect(key).toBe('un');
                else
                    expect(key).toBe('deux');
            });
        });
    });

    describe('stateful operations', function() {
        beforeEach(function() {
            tfidf = new TfIdf();
        	tfidf.addDocument('document one');
        	tfidf.addDocument('document Two');
        });

    	it('should add documents', function() {
            expect(tfidf.documents.length).toBe(2);
            expect(tfidf.documents[0]).toEqual({ document : 1, one : 1 });
            expect(tfidf.documents[1]).toEqual({ document : 1, two : 1 });
    	});

        it('should idf', function() {
            expect(tfidf.idf('document')).toBe(0.8472978603872037);
            expect(tfidf.idf('dumb')).toBe(1.0986122886681098);
        });

        it('should tfidf a single doc', function() {
            expect(tfidf.tfidf('Document', 0)).toBe(0.8472978603872037);
            expect(tfidf.tfidf('one', 0)).toBe(0.9162907318741551);
            expect(tfidf.tfidf('two', 0)).toBe(0);
        });

        it('should tfidfs docs', function() {
            expect(tfidf.tfidfs('two')).toEqual([0, 0.9162907318741551]);
            expect(tfidf.tfidfs('document')).toEqual([0.8472978603872037, 0.8472978603872037]);
        });

        it('should tfidf a single doc on multiple terms', function() {
            expect(tfidf.tfidf('document One', 0)).toBe(0.8472978603872037 + 0.9162907318741551);
            expect(tfidf.tfidf('document two', 0)).toBe(0.8472978603872037);
        });

        it('should store and extract keys', function() {

        });

        it('should tfidfs docs', function() {
            tfidf.tfidfs('two', function(i, tfidf) {
                if(i == 0)
                    expect(tfidf).toBe(0);
                else if (i == 1)
                    expect(tfidf).toBe(0.9162907318741551);
            });
        });
    });
});
<commit_msg>cleaned up tfidf spec<commit_after>/*
Copyright (c) 2011, Rob Ellis, Chris Umbel

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

var TfIdf = require('lib/natural/tfidf/tfidf');
var tfidf;

describe('tfidf', function() {
    describe('stateless operations', function() {
        it('should tf', function() {
            expect(TfIdf.tf('document', { document : 2, one : 1 })).toBe(2);
            expect(TfIdf.tf('document', { greetings : 1, program : 1 })).toBe(0);
            expect(TfIdf.tf('program', { greetings : 1, program : 1 })).toBe(1);
        });
    });

    describe('keys', function() {
        it('should store and recall keys', function() {
            tfidf = new TfIdf();
            tfidf.addDocument('document one', 'un');
        	tfidf.addDocument('document Two', 'deux');

            tfidf.tfidfs('two', function(i, tfidf, key) {
                if(i == 0)
                    expect(key).toBe('un');
                else
                    expect(key).toBe('deux');
            });
        });
    });

    describe('stateful operations', function() {
        beforeEach(function() {
            tfidf = new TfIdf();
            tfidf.addDocument('document one');
            tfidf.addDocument('document Two');
        });

    	it('should add documents', function() {
            expect(tfidf.documents.length).toBe(2);
            expect(tfidf.documents[0]).toEqual({ document : 1, one : 1 });
            expect(tfidf.documents[1]).toEqual({ document : 1, two : 1 });
    	});

        it('should idf', function() {
            expect(tfidf.idf('document')).toBe(0.8472978603872037);
            expect(tfidf.idf('dumb')).toBe(1.0986122886681098);
        });

        it('should tfidf a single doc', function() {
            expect(tfidf.tfidf('Document', 0)).toBe(0.8472978603872037);
            expect(tfidf.tfidf('one', 0)).toBe(0.9162907318741551);
            expect(tfidf.tfidf('two', 0)).toBe(0);
        });

        it('should tfidfs docs', function() {
            expect(tfidf.tfidfs('two')).toEqual([0, 0.9162907318741551]);
            expect(tfidf.tfidfs('document')).toEqual([0.8472978603872037, 0.8472978603872037]);
        });

        it('should tfidf a single doc on multiple terms', function() {
            expect(tfidf.tfidf('document One', 0)).toBe(0.8472978603872037 + 0.9162907318741551);
            expect(tfidf.tfidf('document two', 0)).toBe(0.8472978603872037);
        });

        it('should tfidfs docs', function() {
            tfidf.tfidfs('two', function(i, tfidf) {
                if(i == 0)
                    expect(tfidf).toBe(0);
                else if (i == 1)
                    expect(tfidf).toBe(0.9162907318741551);
            });
        });
    });
});
<|endoftext|>


<commit_before>require('babel/register');

/* DEPENDENCIES */
var gulp = require('gulp'),
    path = require('path'),
    mkdirp = require('mkdirp'),
    glob = require('glob'),
    del = require('del'),
    rename = require('gulp-rename'),
    runSequence = require('run-sequence'),
    size = require('gulp-size'),
    sourcemaps = require('gulp-sourcemaps'),
    eslint = require('gulp-eslint'),
    babelify = require('babelify'),
    browserify = require('browserify'),
    factor = require('factor-bundle'),
    source = require('vinyl-source-stream'),
    uglify = require('gulp-uglify');

/* PATHS */
var src = {
        root: './src/',
        scripts: {
            all : './src/scripts/**/*.js',
            singleEntry: './src/scripts/entry.js',
            multipleEntriesGlob: './src/scripts/*.js'
        },
        templates: './src/templates/*.hbs'
    },
    dest = {
        build: {
            root: './build/',
            scripts: './build/scripts/',
            templates: './build/templates/'
        },
        dist: {
            root: './dist/',
            scripts: './dist/scripts/',
            templates: './dist/templates/'
        }
    };

// Search for JS src files and set `multipleEntries` and `multipleOutputs`
function setSrcScripts() {
    return new Promise(function (resolve, reject) {
        glob(src.scripts.multipleEntriesGlob, function (err, srcFiles) {
            if (err) {
                reject(err);
            }

            src.scripts.multipleEntries = srcFiles.map(function (filePath) {
                return src.root + 'scripts/' + path.basename(filePath);
            });

            src.scripts.multipleOutputs = srcFiles.map(function (filePath) {
                return dest.build.scripts + path.basename(filePath);
            });

            resolve(src.scripts)
        });
    });
}

/* SCRIPTS */
gulp.task('scripts-lint', function () {
    return gulp.src([src.scripts.all])
        .pipe(eslint())
        .pipe(eslint.format());
});

// Bundle for a single entry point.
// Output: single file (`app.js`).
gulp.task('scripts-bundle', function () {
    return browserify({
            entries: src.scripts.singleEntry,
            debug: true
        })
        .transform(babelify)
        .bundle()
        .pipe(source('app.js'))
        .pipe(gulp.dest(dest.build.scripts));
});

// Bundle for multiple entry points.
// Output: different specific files + common modules (`common.js`).
gulp.task('setSrcPath', function (callback) {
    setSrcScripts()
    .then(function () {
        // `factor-bundle` doesn't create output directories so…
        mkdirp(dest.build.scripts, function (err) {
            if (err) {
                throw err;
            }

            callback();
        });
    });
});

gulp.task('scripts-factor-bundle', ['setSrcPath', ], function () {
    return browserify({
            entries: src.scripts.multipleEntries,
            debug: true
        })
        .plugin(factor, {
            outputs: src.scripts.multipleOutputs
        })
        .transform(babelify)
        .bundle()
        .pipe(source('common.js'))
        .pipe(gulp.dest(dest.build.scripts));
});

gulp.task('scripts-minify', function () {
    return gulp.src(dest.build.scripts + '*.js')
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .pipe(size({
            title: 'Minified JS',
            pretty: true
        }))
        .pipe(rename(function (path) {
            path.basename += ".min";
        }))
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest(dest.dist.scripts));
});

/* BUILD */
gulp.task('scripts-build', function () {
    runSequence('scripts-lint', 'scripts-factor-bundle');
});

gulp.task('clean-build', function () {
    del.sync([dest.build.root + '*']);
});

gulp.task('build', function () {
    runSequence('clean-build', 'scripts-build');
});

/* DIST */
gulp.task('scripts-dist', function () {
    runSequence('scripts-minify');
});

gulp.task('clean-dist', function () {
    del.sync([dest.dist.root + '*']);
});

gulp.task('dist', function () {
    runSequence('clean-dist', 'scripts-dist');
});

/* WATCH */
gulp.task('watch', function () {
    gulp.start('build')
    gulp.watch(src.scripts.all, ['scripts-build']);
});
<commit_msg>Remove templates path<commit_after>require('babel/register');

/* DEPENDENCIES */
var gulp = require('gulp'),
    path = require('path'),
    mkdirp = require('mkdirp'),
    glob = require('glob'),
    del = require('del'),
    rename = require('gulp-rename'),
    runSequence = require('run-sequence'),
    size = require('gulp-size'),
    sourcemaps = require('gulp-sourcemaps'),
    eslint = require('gulp-eslint'),
    babelify = require('babelify'),
    browserify = require('browserify'),
    factor = require('factor-bundle'),
    source = require('vinyl-source-stream'),
    uglify = require('gulp-uglify');

/* PATHS */
var src = {
        root: './src/',
        scripts: {
            all : './src/scripts/**/*.js',
            singleEntry: './src/scripts/entry.js',
            multipleEntriesGlob: './src/scripts/*.js'
        }
    },
    dest = {
        build: {
            root: './build/',
            scripts: './build/scripts/'
        },
        dist: {
            root: './dist/',
            scripts: './dist/scripts/'
        }
    };

// Search for JS src files and set `multipleEntries` and `multipleOutputs`
function setSrcScripts() {
    return new Promise(function (resolve, reject) {
        glob(src.scripts.multipleEntriesGlob, function (err, srcFiles) {
            if (err) {
                reject(err);
            }

            src.scripts.multipleEntries = srcFiles.map(function (filePath) {
                return src.root + 'scripts/' + path.basename(filePath);
            });

            src.scripts.multipleOutputs = srcFiles.map(function (filePath) {
                return dest.build.scripts + path.basename(filePath);
            });

            resolve(src.scripts)
        });
    });
}

/* SCRIPTS */
gulp.task('scripts-lint', function () {
    return gulp.src([src.scripts.all])
        .pipe(eslint())
        .pipe(eslint.format());
});

// Bundle for a single entry point.
// Output: single file (`app.js`).
gulp.task('scripts-bundle', function () {
    return browserify({
            entries: src.scripts.singleEntry,
            debug: true
        })
        .transform(babelify)
        .bundle()
        .pipe(source('app.js'))
        .pipe(gulp.dest(dest.build.scripts));
});

// Bundle for multiple entry points.
// Output: different specific files + common modules (`common.js`).
gulp.task('setSrcPath', function (callback) {
    setSrcScripts()
    .then(function () {
        // `factor-bundle` doesn't create output directories so…
        mkdirp(dest.build.scripts, function (err) {
            if (err) {
                throw err;
            }

            callback();
        });
    });
});

gulp.task('scripts-factor-bundle', ['setSrcPath', ], function () {
    return browserify({
            entries: src.scripts.multipleEntries,
            debug: true
        })
        .plugin(factor, {
            outputs: src.scripts.multipleOutputs
        })
        .transform(babelify)
        .bundle()
        .pipe(source('common.js'))
        .pipe(gulp.dest(dest.build.scripts));
});

gulp.task('scripts-minify', function () {
    return gulp.src(dest.build.scripts + '*.js')
        .pipe(sourcemaps.init({loadMaps: true}))
        .pipe(uglify())
        .pipe(size({
            title: 'Minified JS',
            pretty: true
        }))
        .pipe(rename(function (path) {
            path.basename += ".min";
        }))
        .pipe(sourcemaps.write('./'))
        .pipe(gulp.dest(dest.dist.scripts));
});

/* BUILD */
gulp.task('scripts-build', function () {
    runSequence('scripts-lint', 'scripts-factor-bundle');
});

gulp.task('clean-build', function () {
    del.sync([dest.build.root + '*']);
});

gulp.task('build', function () {
    runSequence('clean-build', 'scripts-build');
});

/* DIST */
gulp.task('scripts-dist', function () {
    runSequence('scripts-minify');
});

gulp.task('clean-dist', function () {
    del.sync([dest.dist.root + '*']);
});

gulp.task('dist', function () {
    runSequence('clean-dist', 'scripts-dist');
});

/* WATCH */
gulp.task('watch', function () {
    gulp.start('build')
    gulp.watch(src.scripts.all, ['scripts-build']);
});
<|endoftext|>


<commit_before>const Discord = require("discord.js");
const log4js = require("log4js");
const fs = require("fs");

const client = new Discord.Client();

log4js.configure({
  appenders: {
    out: { type: "console" },
    app: { type: "file", filename: "logs/info.log"},
  },
  categories: {
    default: { appenders: [ "out", "app" ], level: "trace" }
  }
});

const logger = log4js.getLogger("default");

logger.info("Starting Bot");

let initConfig = ( () => {
  const initconfig = `\{
    \"name\": \"ninibot\"\,
    \"version\": \"1.0.0\"\,
    \"game\": \"The game the bot will be playing\"\,
    \"token\": \"put your token here\"\,
    \"ownerID\": \"ID of whoever will run the bot\",
    \"prefix\": \"!\"\,
    \"mainChannel\": \"The channel id to delete things from\"\,
    \"backupChannel\": \"The channel id to back deleted things up in\"\,
    \"cleanupTime\": 60000\,
    \"modrole\": \"The role id of moderators\"\,
    \"adminrole\": \"The role id of admins\"\,
    \"topic\": \"No topic set\"\n}`;
  fs.writeFileSync("./config.json", initconfig, "utf8", (err) => {if (err) logger.error(err);});
  logger.info("Config was not found or malformed. Set up default config. Please configure then restart");
  process.exit(1);
});

var config;
try {
  config = require("./config.json");
} catch (err) {
  logger.error(err);
  initConfig();
}

client.login(config.token);

let cleanup = ( () => {
  let mainchat = client.channels.get(config.mainChannel);
  let backuproom = client.channels.get(config.backupChannel);

  //If we process more than 100, we should do it again to make sure we got everything
  let count = 0;

  mainchat.fetchMessages({limit:100})
          .then(messages => {
            let messagesArr = messages.array();
            let messageCount = messagesArr.length;

            for(let i = messageCount - 1; i >= 0; i--) {
              if (Date.now() - messagesArr[i].createdAt < config.cleanupTime) return;
              backuproom.send(`${messagesArr[i].author.tag}@${messagesArr[i].createdAt.getHours()}:${messagesArr[i].createdAt.getMinutes()}: ${messagesArr[i].content}`).catch( (err) => logger.error(err));
              messagesArr[i].delete()
                .then(() => {
                  count += 1;
                  if(count >= 100) {
                    cleanup();
                  }
                })
                .catch((err) => {
                  logger.error(err);
                  count += 1;
                  if(count >= 100) {
                    cleanup();
                  }
                });
            }
          })
          .catch((err) => logger.error(err));
});

//Authorisation Check for Mod and up
//TODO:Fix this
let authCheck = ( (message, operation) => {
  //Auth check
  let modrole = message.guild.roles.get(config.modrole);
  let adminrole = message.guild.roles.get(config.adminrole);
  if (!(message.member.roles.has(modrole.id)||message.member.roles.has(adminrole.id))){
    message.channel.send(`Sorry, you do not have permission to ${operation}`);
    return false;
  }
  return true;
});

let saveConfig = ( () => {
  fs.writeFile("./config.json", JSON.stringify(config), "utf8", (err) => {if (err) logger.error(err);});
});

client.on("ready", () => {
  logger.info(`Bot Ready - ${config.name} - ${config.version}`);
  client.user.setGame(config.game);

  setInterval(cleanup, 30000);
});

client.on("message", (message) => {
  if (!message.content.startsWith(config.prefix) || message.author.bot) return;

  //Reject PMs and leave channel
  if (message.channel.type != "text") {
    message.channel.send("Sorry, this bot currently does not support direct messages");
    message.channel.delete().catch((err) => logger.error(err));
    return;
  }

  let command = message.content.toLowerCase().split(" ")[0].slice(1);

  //Check privacy time
  if (command === "privacy"){
    message.channel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
  }

  //Get Weekly Topic
  if (command === "topic"){
    message.channel.send(config.topic).catch((err) => logger.error(err));
  }

  //Set User Nickname (eg. to add pronouns)
  if (command === "nick"){
    let userNick = message.content.slice(message.content.indexOf(" ")+1);
    logger.info(`Setting nickname for "${message.member.user.tag}" to "${userNick}"`);
    message.member.setNickname(userNick).then().catch((err) => logger.error(err));
  }

  //---Priveleged Commands---

  //Set Weekly Topic
  if (command === "settopic"){
    if (!authCheck(message, "set the topic")) return;

    config.topic = message.content.slice(message.content.indexOf(" ")+1);
    saveConfig();
    message.channel.send(`Set topic to "${config.topic}"`).catch((err) => logger.error(err));
  }

  //Set time after which to delete old messages
  if (command === "setcleanuptime"){
    if (!authCheck(message, "set the cleanup time")) return;
    let timeout = +message.content.slice(message.content.indexOf(" ")+1);
    if (isNaN(timeout) || timeout > 20000 || timeout < 1 || (timeout % 1 != 0)) {
      message.channel.send(`Cannot set timeout to "${timeout}"`);
    }
    timeout *= 60000;
    config.cleanupTime = timeout;
    saveConfig();
    message.channel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
  }

  //---Owner Only Commands---

  //Set Bot Game
  if (command === "setgame"){
    if(message.author.id !== config.ownerID) return;
    let newGame = message.content.slice(message.content.indexOf(" ")+1);
    logger.info(`Setting game to "${newGame}"`);
    config.game = newGame;
    client.user.setGame(newGame);
    saveConfig();
  }

});

client.on("guildMemberAdd", (member) => {
  logger.info(`New User ${member.user.username} has joined ${member.guild.name}` );
  member.guild.defaultChannel.send(`Welcome ${member.displayName} to ${member.guild.name}!`);
  member.guild.defaultChannel.send("Message a moderator or admin to get permission to talk");
  member.guild.defaultChannel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
});
<commit_msg>Undeleted package.json - oops<commit_after>const Discord = require("discord.js");
const log4js = require("log4js");
const fs = require("fs");

const client = new Discord.Client();
//Set up logging
log4js.configure({
  appenders: {
    out: { type: "console" },
    app: { type: "file", filename: "logs/info.log"},
  },
  categories: {
    default: { appenders: [ "out", "app" ], level: "trace" }
  }
});

const logger = log4js.getLogger("default");

logger.info("Starting Bot");

//Config in case none is present
let initConfig = ( () => {
  const initconfig = `\{
    \"name\": \"ninibot\"\,
    \"version\": \"1.0.0\"\,
    \"game\": \"The game the bot will be playing\"\,
    \"token\": \"put your token here\"\,
    \"ownerID\": \"ID of whoever will run the bot\",
    \"prefix\": \"!\"\,
    \"mainChannel\": \"The channel id to delete things from\"\,
    \"backupChannel\": \"The channel id to back deleted things up in\"\,
    \"cleanupTime\": 60000\,
    \"modrole\": \"The role id of moderators\"\,
    \"adminrole\": \"The role id of admins\"\,
    \"topic\": \"No topic set\"\n}`;
  fs.writeFileSync("./config.json", initconfig, "utf8", (err) => {if (err) logger.error(err);});
  logger.info("Config was not found or malformed. Set up default config. Please configure then restart");
  process.exit(1);
});

//Load config, or if we can't, set one up and quit
var config;
try {
  config = require("./config.json");
} catch (err) {
  logger.error(err);
  initConfig();
}

//Log in to Discord
client.login(config.token);

//Delete old messages
let cleanup = ( () => {
  let mainchat = client.channels.get(config.mainChannel);
  let backuproom = client.channels.get(config.backupChannel);

  //If we process more than 100, we should do it again to make sure we got everything
  let count = 0;

  mainchat.fetchMessages({limit:100})
          .then(messages => {
            let messagesArr = messages.array();
            let messageCount = messagesArr.length;

            for(let i = messageCount - 1; i >= 0; i--) {
              if (Date.now() - messagesArr[i].createdAt < config.cleanupTime) return;
              backuproom.send(`${messagesArr[i].author.tag}@${messagesArr[i].createdAt.getHours()}:${messagesArr[i].createdAt.getMinutes()}: ${messagesArr[i].content}`).catch( (err) => logger.error(err));
              messagesArr[i].delete()
                .then(() => {
                  count += 1;
                  if(count >= 100) {
                    cleanup();
                  }
                })
                .catch((err) => {
                  logger.error(err);
                  count += 1;
                  if(count >= 100) {
                    cleanup();
                  }
                });
            }
          })
          .catch((err) => logger.error(err));
});

//Authorisation Check for Mod and up
let authCheck = ( (message, operation) => {
  //Auth check
  let modrole = message.guild.roles.get(config.modrole);
  let adminrole = message.guild.roles.get(config.adminrole);
  if (!(message.member.roles.has(modrole.id)||message.member.roles.has(adminrole.id))){
    message.channel.send(`Sorry, you do not have permission to ${operation}`);
    return false;
  }
  return true;
});

//Save the config file
let saveConfig = ( () => {
  fs.writeFile("./config.json", JSON.stringify(config), "utf8", (err) => {if (err) logger.error(err);});
});

//When the bot has logged in to Discord
client.on("ready", () => {
  logger.info(`Bot Ready - ${config.name} - ${config.version}`);
  client.user.setGame(config.game);

  setInterval(cleanup, 30000);
});

//Handle messages
client.on("message", (message) => {
  if (!message.content.startsWith(config.prefix) || message.author.bot) return;

  //Reject PMs and leave channel
  if (message.channel.type != "text") {
    message.channel.send("Sorry, this bot currently does not support direct messages");
    message.channel.delete().catch((err) => logger.error(err));
    return;
  }

  let command = message.content.toLowerCase().split(" ")[0].slice(1);

  //Check privacy time
  if (command === "privacy"){
    message.channel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
  }

  //Get Weekly Topic
  if (command === "topic"){
    message.channel.send(config.topic).catch((err) => logger.error(err));
  }

  //Set User Nickname (eg. to add pronouns)
  if (command === "nick"){
    let userNick = message.content.slice(message.content.indexOf(" ")+1);
    logger.info(`Setting nickname for "${message.member.user.tag}" to "${userNick}"`);
    message.member.setNickname(userNick).then().catch((err) => logger.error(err));
  }

  //---Priveleged Commands---

  //Set Weekly Topic
  if (command === "settopic"){
    if (!authCheck(message, "set the topic")) return;

    config.topic = message.content.slice(message.content.indexOf(" ")+1);
    saveConfig();
    message.channel.send(`Set topic to "${config.topic}"`).catch((err) => logger.error(err));
  }

  //Set time after which to delete old messages
  if (command === "setcleanuptime"){
    if (!authCheck(message, "set the cleanup time")) return;
    let timeout = +message.content.slice(message.content.indexOf(" ")+1);
    if (isNaN(timeout) || timeout > 20000 || timeout < 1 || (timeout % 1 != 0)) {
      message.channel.send(`Cannot set timeout to "${timeout}"`);
    }
    timeout *= 60000;
    config.cleanupTime = timeout;
    saveConfig();
    message.channel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
  }

  //---Owner Only Commands---

  //Set Bot Game
  if (command === "setgame"){
    if(message.author.id !== config.ownerID) return;
    let newGame = message.content.slice(message.content.indexOf(" ")+1);
    logger.info(`Setting game to "${newGame}"`);
    config.game = newGame;
    client.user.setGame(newGame);
    saveConfig();
  }

});

//Welcome new members
client.on("guildMemberAdd", (member) => {
  logger.info(`New User ${member.user.username} has joined ${member.guild.name}` );
  member.guild.defaultChannel.send(`Welcome ${member.displayName} to ${member.guild.name}!`);
  member.guild.defaultChannel.send("Message a moderator or admin to get permission to talk");
  member.guild.defaultChannel.send(`Messages in this channel are removed after ${config.cleanupTime / 60000} minutes for privacy.`);
});
<|endoftext|>