<|start_filename|>src/auth.js<|end_filename|>
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './Routes/auth';

Vue.use(VueRouter);

// Global Components
import Auth from './Views/Layouts/Auth.vue';

Vue.component('auth', Auth);

const router = new VueRouter({
	routes,
});

const app = new Vue({
	router,
}).$mount('#appRoot');

window.SteamClient = require('electron').remote.app.SteamClient;


<|start_filename|>src/Steam/chatManager.js<|end_filename|>
const EventEmitter = require('events');
const SteamID      = require('steamid');
const moment       = require('moment');

class ChatManager extends EventEmitter {
	constructor(parent) {
		super();

		this._parent = parent;
		this._chats  = [];

		this.bindEvents();
	}

	bindEvents() {
		this._parent.client.on('friendOrChatMessage', (senderID, message, room) => {
			const chat = this.getChat(room.accountid);

			if (!chat)
				this._chats.push(this._createChat(room));

			chat.messages.push({
				sender   : 'other',
				message,
				timestamp: Date.now(),
				readAt   : null,
			});

			this._parent.emit('chatMessage', senderID, message, room);
		});

		this._parent.client.on('friendTyping', (senderID) => this.userTyping(senderID));
	}

	userTyping(senderID, clear = false) {
		const chat = this.getChat(senderID.accountid);

		if (!chat) return;

		const steamid64 = senderID.getSteamID64();
		let typee       = chat.typers.find(t => t.steamid64 === steamid64);

		if (!typee) {
			chat.typers.push({
				steamid64,
				nickname: this._parent.getUser(steamid64).nickname,
				timer   : null,
			});

			typee = chat.typers[ chat.typers.length - 1 ];
		}

		this._parent.emit(`friendTyping#${steamid64}`, clear);

		if (typee && typee.timer) {
			clearTimeout(typee.timer);
			typee.timer = null;
		}

		if (clear) return;

		typee.timer = setTimeout(() => this.userTyping(senderID, true), 2500);
	}

	getChat(accountid) {
		return this.chats.find(c => c.room.accountid.toString() === accountid.toString());
	}

	sendMessage(steamid, roomid, content) {
		const self = this;
		this._parent.client.chatMessage(steamid, content);

		new Promise(() => {
			const chat = self.getChat(roomid);

			if (chat) {
				chat.messages.push({
					sender   : 'self',
					message  : content,
					timestamp: Date.now(),
					readAt   : Date.now(),
				});

				self._parent.emit('chatMessage', this._parent.steamID64, content, chat.room);
			}
		});

		return true;
	}

	sendTyping(roomid) {
		const chat = this.getChat(roomid);

		if (chat)
			this._parent.client.chatTyping(chat.room.getSteamID64());
	}

	clearUnreadChat() {
		this.chats.forEach(chat => {
			chat.messages.forEach(message => {
				message.readAt = Date.now()
			});
		});
	}

	createChat(steamid) {
		const steamID = new SteamID(steamid.toString());

		return new Promise((resolve, reject) => {
			// DOES FUCKING NOTHING
			//this._parent.client.joinChat(steamID, (result, chatID) => {
			this._parent.client.createChatRoom(steamID, this._parent.client.steamID, (result, chatID) => {
				this._chats.push(this._createChat(steamID));

				this.getChatHistory(steamID.accountid, steamID);

				resolve({ result, chatRoom: this._chats[ this._chats.length - 1 ] });
			});
		});
	}

	getChatHistory(accountid, steamid) {
		this._parent.client.getChatHistory(steamid, (result, messages) => {
			const chat = this.getChat(accountid);

			if (chat) {
				messages.forEach(message => {
					chat.messages.push({
						sender   : (message.steamID.getSteamID64() === this._parent.steamID64 ? 'self' : 'other'),
						message  : message.message,
						timestamp: +moment(message.timestamp),
						readAt   : (message.unread ? null : Date.now()),
					});
				});
			}

			this._parent.emit('chatMessage');
		});
	}

	_createChat(steamID) {
		const self = this;

		return {
			room    : steamID,
			get sender() {
				return self._parent.getUser(this.room.getSteamID64());
			},
			messages: [],
			typers  : [],
			typeeMessage(maxlength = 2) {
				if (this.typers.length <= 0) return null;
				const typers = this.typers.filter(t => t.timer !== null);
				if (typers.length > maxlength) return `${typers.length} people are typing...`;
				return typers.map(t => t.nickname).join(',') + ' is typing...'; // Going to have a problem here...
			},
			get lastMessage() {
				if (this.messages.length <= 0) return null;
				const _msgs = this.messages.filter(m => m.sender === 'other');
				return (_msgs.length <= 0 ? null : _msgs[ 0 ]);
			},
			get unreadCount() {
				const lMessage = this.lastMessage;
				if (!lMessage) return 0;
				return this.messages.filter(m => !m.readAt).length;
			},
		};
	}

	// Getters
	get chats() {
		return this._chats;
	}

	get unreadChatMessages() {
		return this.chats.reduce((sum, chat) => sum + chat.unreadCount, 0);
	}
}

module.exports = ChatManager;


<|start_filename|>src/Steam/steamCMD.js<|end_filename|>
const { app }      = require('electron');
const EventEmitter = require('events');
const request      = require('request');
const child        = require('child-process-promise');
const path         = require('path');
const fs           = require('fs');

const DEFAULT_OPTIONS = {
	steamcmd_url: 'https://steamcdn-a.akamaihd.net/client/installer/',
	install_path: path.join(app.getAppPath(), 'steamcmd'),
};

class SteamCMD extends EventEmitter {
	constructor(options = {}) {
		super();

		this._ready        = false;
		this._process      = null;
		this._platform     = null;
		this._isTouch      = false;
		this._outputBuffer = '';

		this.options = Object.assign({}, DEFAULT_OPTIONS, options);

		this.init();
	}

	_preInit() {
		let fileName = null;

		switch (process.platform) {
			case 'win32':
				this._platform = 'windows';
				fileName       = 'steamcmd.zip';
				break;
			case 'darwin':
				this._platform = 'mac';
				fileName       = 'steamcmd_osx.tar.gz';
				break;
			case 'linux':
				this._platform = 'linux';
				fileName       = 'steamcmd_linux.tar.gz';
				break;
			default:
				throw 'Unsupported Platform!';
		}

		// The steamCMD directory doesn't exist
		try {
			fs.accessSync(this.options.install_path, fs.constants.F_OK);
		} catch (e) {
			fs.mkdirSync(this.options.install_path);
		}

		// If steamcmd.exe doesn't exist - download it
		return new Promise((resolve, reject) => {
			try {
				const executablePath = path.join(this.options.install_path, 'steamcmd.' + (this._platform === 'windows' ? 'exe' : 'sh'));
				fs.accessSync(executablePath, fs.constants.R_OK);

				this.touch(executablePath)
				    .then(resolve)
				    .catch(reject);
			} catch (e) {
				this._download(fileName)
				    .then(filePath => {
					    this._install(filePath)
					        .then(() => {
						        this.touch()
						            .then(resolve)
						            .catch(reject);
					        })
					        .catch(reject);
				    })
				    .catch(reject);
			}
		});
	}

	_cleanup() {
		try {
			fs.rmdirSync(this.options.install_path);
			return true;
		} catch (e) {
			return e;
		}
	}

	_download(fileName) {
		return new Promise((resolve, reject) => {
			try {
				const cleanupStatus = this._cleanup();

				if (cleanupStatus !== true)
					return reject(cleanupStatus);

				const filePath = path.join(this.options.install_path, fileName);

				this.emit('download', true);

				request(this.options.steamcmd_url + fileName)
					.pipe(fs.createWriteStream(filePath))
					.on('close', () => {
						resolve(filePath);
						this.emit('download', false);
					});
			} catch (e) {
				this.emit('download', false);
				reject(e);
			}
		});
	}

	_install(filePath) {
		return new Promise((resolve, reject) => {
			this.emit('install', true);

			try {
				if (this._platform === 'windows') {
					fs.createReadStream(filePath)
					  .pipe(require('unzip').Extract({
						  path: this.options.install_path,
					  }))
					  .on('close', () => {
						  this.emit('install', false);
						  resolve();
					  });
				} else {
					(require('tar'))
						.x({
							file: filePath,
							C   : this.options.install_path,
						})
						.then(() => {
							this.emit('install', false);
							resolve();
						});
				}
			} catch (e) {
				this.emit('install', false);
				reject(e);
			}
		});
	}

	touch() {
		return this.launch('login anonymous');
	}

	launch(args = []) {
		if (typeof args === 'string')
			args = [ args ];

		return new Promise((resolve, reject) => {
			args.unshift('@ShutdownOnFailedCommand 0');
			args.push('find e', 'quit');
			args = args.map(a => '+' + a);

			child.spawn((this._platform === 'windows' ? 'steamcmd.exe' : 'steamcmd.sh'), args, {
				capture: [ 'stdout', 'stderr' ],
				cwd    : this.options.install_path,
			})
			     .then(data => {
				     let response = data.stdout.substr(0, data.stdout.indexOf('ConVars:\r\n'));
				     /*const truncateString = '"@ShutdownOnFailedCommand" = "0"';
				     const truncateIndex  = response.indexOf(truncateString);

				     if (truncateIndex >= 0) {
					     response = response.substr(truncateIndex + truncateString.length,
						     (response.length - truncateIndex) - truncateString.length);
				     }*/

				     response = response.replace('"@ShutdownOnFailedCommand" = "0"', '');

				     resolve(response.trim());
			     })
			     .catch(reject);
		});
	}

	init() {
		this._preInit()
		    .then(() => {
			    this._ready = true;
			    this.emit('ready');
		    })
		    .catch(err => this.emit('error', err));
	}

	// Helper functions
	installedApps() {
		return new Promise((resolve, reject) => {
			this.launch([ 'login anonymous', 'apps_installed' ])
			    .then(output => {
				    const data = {
					    count: 0,
					    list : [],
				    };

				    const appCount = output.match(/(\d+) apps installed :/i);
				    if (appCount) data.count = parseInt(appCount[ 1 ]);

				    const appRegex = /^AppID (\d+) : "(.*)" : (.*)$/igm;
				    let match      = appRegex.exec(output);

				    while (match) {
					    data.list.push({
						    id  : parseInt(match[ 1 ]),
						    name: match[ 2 ],
						    path: match[ 3 ],
					    });

					    match = appRegex.exec(output);
				    }

				    resolve(data);
			    })
			    .catch(reject);
		});
	}

	appStatus(appid) {
		return new Promise((resolve, reject) => {
			this.launch([ 'login anonymous', `app_status ${appid}` ])
			    .then(output => {
				    resolve(output);
			    })
			    .catch(reject);
		});
	}

	appUpdate(appid) {
		return new Promise((resolve, reject) => {
			this.launch([ 'login anonymous', `app_update ${appid} -validate` ])
			    .then(data => {
				    console.log(data);

				    resolve(null);
			    })
			    .catch(console.error);
		});
	}

	// Getters
	get process() {
		return this._process;
	}

	get isTouch() {
		return this._isTouch;
	}

	get isReady() {
		return this._ready;
	}
}

module.exports = SteamCMD;


<|start_filename|>src/friends.js<|end_filename|>
import Vue from 'vue';
import VueRouter from 'vue-router';
import routes from './Routes/friends';

Vue.use(VueRouter);

// Global Components
import Friends from './Views/Layouts/Friends.vue';

Vue.component('friends', Friends);

// Global Packages
window.$ = window.jQuery = require('jquery');
window.SteamClient = require('electron').remote.app.SteamClient;

const router = new VueRouter({
	routes,
});

const app = new Vue({
	router,
}).$mount('#appRoot');

window.SteamClient = require('electron').remote.app.SteamClient;

$(document).click((e) => {
	$('.v-dropdown').each(function () {
		if ($(e.target).parents('.v-dropdown').get(0) === this) return;
		this.__vue__.close();
	});
});


<|start_filename|>main.js<|end_filename|>
const { app, BrowserWindow, ipcMain, Tray, Menu } = require('electron');
const url                                         = require('url');
const path                                        = require('path');
const fs                                          = require('fs');
const SteamUser                                   = require('steam-user');

let SteamClient, window, tray;
let clientWindow;

const DEV_ENV = (process.env.NODE_ENV === 'development');

function createWindow() {
    window = new BrowserWindow({
        width : 400,
        height: 600,
        title : 'TecnoSteam',
        frame : false,
        show  : false,
    });

    if (DEV_ENV) {
        window.loadURL('http://localhost:3000/electron-views/auth.html');
        //window.webContents.openDevTools();
    } else {
        window.loadURL(url.format({
            pathname: path.join(__dirname, 'electron-views', 'auth.html'),
            protocol: 'file:',
            slashes : true,
        }));
    }

    window.on('ready-to-show', () => window.show());

    app.SteamClient = SteamClient = new (require('./src/Steam'))();

    app.SteamClient.on('loggedOn', (details, parental) => {
        if (clientWindow)
            return clientWindow.show();

        clientWindow = new BrowserWindow({
            width : 1600,
            height: 900,
            title : 'TecnoSteam',
            frame : false,
            show  : false,
        });

        if (!tray) {
            tray = new Tray('./steam.ico');

            tray.on('double-click', () => clientWindow.show());

            tray.setToolTip('TecnoSteam');
            tray.setContextMenu(Menu.buildFromTemplate([
                {
                    label: 'Exit',
                    click() {
                        app.isQuiting = true;
                        app.quit();
                    },
                },
            ]));
        }

        if (DEV_ENV) {
            clientWindow.loadURL('http://localhost:3000/electron-views/index.html');
            //clientWindow.webContents.openDevTools();
        } else {
            clientWindow.loadURL(url.format({
                pathname: path.join(__dirname, 'electron-views', 'index.html'),
                protocol: 'file:',
                slashes : true,
            }));
        }

        clientWindow.on('close', e => {
            if (!app.isQuiting) {
                e.preventDefault();
                clientWindow.hide();
            }
        });
        clientWindow.on('ready-to-show', () => clientWindow.show());

        if (window) window.close();
    });

    window.on('closed', () => window = null);
}

app.on('ready', createWindow);
app.on('window-all-closed', () => {
    if (process.platform !== 'darwin')
        app.quit();
});
app.on('activate', () => {
    if (window === null)
        createWindow();
});

// Setup directory structures
const userData       = app.getPath('userData');
const cacheDirectory = [
    path.join(userData, 'cache'),
    'achievements',
    'screenshots',
    'users',
];

fs.access(cacheDirectory[ 0 ], fs.constants.F_OK, (err) => {
    if (err !== null)
        fs.mkdir(cacheDirectory[ 0 ]);
});

for (let i = 1; i < cacheDirectory.length; i++) {
    const filePath = path.join(cacheDirectory[ 0 ], cacheDirectory[ i ]);

    fs.access(filePath, fs.constants.F_OK, (err) => {
        if (err !== null)
            fs.mkdir(filePath);
    });
}

// Events
ipcMain.on('scCreateAccount', (e, data) => {
    SteamClient.createAccount(data.accountName, data.password, data.email, (result, steamid) => {
        ipcMain.emit('scCreateAccount', {
            success        : (result === SteamUser.EResult.OK),
            steamid,
            duplicateName  : (result === SteamUser.EResult.DuplicateName),
            illegalPassword: (result === SteamUser.EResult.IllegalPassword),
            unknownError   : (result !== SteamUser.EResult.OK &&
                              result !== SteamUser.EResult.DuplicateName &&
                              result !== SteamUser.EResult.IllegalPassword),
        });
    });
});

let chatWindow   = null;
let friendWindow = null;

ipcMain.on('createChatWindow', (e, data) => {
    if (!chatWindow) {
        chatWindow = new BrowserWindow({
            width : 400,
            height: 500,
            title : 'TecnoSteam - Chat',
            frame : false,
            show  : false,
        });

        const HASH = (data.accountid ? '#/' + data.accountid : '');

        if (process.env.NODE_ENV === 'development') {
            chatWindow.loadURL(`http://localhost:3000/electron-views/chat.html${HASH}`);
        } else {
            chatWindow.loadURL(url.format({
                pathname: path.join(__dirname, 'electron-views', `index.html${HASH}`),
                protocol: 'file:',
                slashes : true,
            }));
        }

        chatWindow.on('closed', () => chatWindow = null);
        chatWindow.on('ready-to-show', () => chatWindow.show());

        ipcMain.emit('createChatWindow', { chatWindow });
    } else
        chatWindow.focus();
});

ipcMain.on('createFriendWindow', (e) => {
    if (!friendWindow) {
        friendWindow = new BrowserWindow({
            width : 300,
            height: 450,
            title : 'TecnoSteam - Friends',
            frame : false,
            show  : false,
        });

        if (DEV_ENV) {
            friendWindow.loadURL('http://localhost:3000/electron-views/friends.html');
        } else {
            friendWindow.loadURL(url.format({
                pathname: path.join(__dirname, 'electron-views', 'friends.html'),
                protocol: 'file:',
                slashes : true,
            }));
        }

        friendWindow.on('closed', () => friendWindow = null);
        friendWindow.on('ready-to-show', () => friendWindow.show());

        ipcMain.emit('createFriendWindow', { friendWindow });
    } else
        friendWindow.focus();
});



<|start_filename|>src/app.js<|end_filename|>
import Vue from 'vue';
import VueRouter from 'vue-router';
import store from './Store';
import routes from './Routes/app';
import directives from './Directives';

Vue.use(VueRouter);

// Global Components
import App from './Views/Layouts/App.vue';

Vue.component('app', App);
// --- END ---

// Global Packages
window.$ = window.jQuery = require('jquery');
window.SteamClient = require('electron').remote.app.SteamClient;

const router = new VueRouter({
	routes,
});

const app = new Vue({
	router,
	store,
	computed: {
		app() {
			return this.$children[ 0 ];
		},
	},
}).$mount('#appRoot');

// Hook into Steam interface
SteamClient.on('friendsList', console.log);
SteamClient.on('users', console.log);

// Initiate steam login
//SteamClient.clientReady();

$(document).click((e) => {
	$('.v-dropdown').each(function () {
		if ($(e.target).parents('.v-dropdown').get(0) === this) return;
		this.__vue__.close();
	});
});


<|start_filename|>src/Store/utils/api.js<|end_filename|>
import axios from 'axios';
import JSONbig from 'json-bigint';

const API_ROOT      = 'http://tecnosteam.dev/api/'; // MODIFY HERE
const axiosInstance = axios.create({
	baseURL: API_ROOT,
	headers: {
		'Accept'          : 'application/json',
		'X-Requested-With': 'XMLHttpRequest',
		'Authorization'   : 'Bearer [INSERT PASSPORT TOKEN HERE]', // MODIFY HERE
	},
	transformResponse(data) {
		return JSONbig.parse(data);
	},
});

function resolveURL(endpoint) {
	while (endpoint.startsWith('/'))
		endpoint = endpoint.substr(1, endpoint.length - 1);
	return API_ROOT + endpoint;
}

export default {
	get(api_endpoint, request) {
		return axiosInstance.get(resolveURL(api_endpoint), request)
		                    .then(response => Promise.resolve(response))
		                    .catch(error => Promise.reject(error));
	},
	post(api_endpoint, request) {
		return axiosInstance.post(resolveURL(api_endpoint), request)
		                    .then(response => Promise.resolve(response))
		                    .catch(error => Promise.reject(error));
	},
	patch(api_endpoint, request) {
		return axiosInstance.post(resolveURL(api_endpoint), request)
		                    .then(response => Promise.resolve(response))
		                    .catch(error => Promise.reject(error));
	},
	delete(api_endpoint, request) {
		return axiosInstance.delete(resolveURL(api_endpoint), request)
		                    .then(response => Promise.resolve(response))
		                    .catch(error => Promise.reject(error));
	},
}


<|start_filename|>webpack.config.js<|end_filename|>
const path    = require('path');
const webpack = require('webpack');
const plugins = require('webpack-load-plugins')();

module.exports = {
	entry  : {
		app    : './src/app.js',
		auth   : './src/auth.js',
		chat   : './src/chat.js',
		friends: './src/friends.js',
	},
	output : {
		path      : path.resolve(__dirname, './dist'),
		publicPath: '/dist/',
		filename  : '[name].js',
	},
	resolve: {
		alias: {
			vue: 'vue/dist/vue.js',
		},
	},
	plugins,
	target : 'electron-renderer',
	module : {
		loaders: [
			{
				test  : /\.vue$/,
				loader: 'vue-loader',
			},
			{
				test   : /\.js$/,
				loader : 'babel-loader',
				exclude: /node_modules/,
			},
			{
				test  : /\.(png|jpg|gif|svg|jpeg)$/,
				loader: 'file',
				query : {
					name: '[name].[ext]?[hash]',
				},
			},
		],
	},
};

if (process.env.NODE_ENV === 'production') {
	module.exports.plugins = [
		new webpack.DefinePlugin({
			'process.env': {
				NODE_ENV: '"production"',
			},
		}),
		new webpack.optimize.UglifyJsPlugin({
			compress: {
				warnings: false,
			},
		}),
	];
} else {
	module.exports.devtool = '#source-map';
}


<|start_filename|>src/Store/modules/library.js<|end_filename|>
import api from '../utils/api';
import cache from '../../cache';
import library from '../utils/library';
import {
	LIBRARY_SEARCH,
	LIBRARY_LIST,
	REQUEST_LIBRARY_LIST,
	GET_LIBRARY_LIST_FAILURE,
	ADD_LIBRARY_ACHIEVEMENTS,
	UPDATE_GAME_SCREENSHOT,
	UPDATE_GAME_ACHIEVEMENT,
	UPDATE_GAME_INSTALL_STATE,
	ADD_LIBRARY_NEWS,
} from '../types';

const state = {
	loading     : false,
	searchQuery : null,
	games       : [],
	achievements: {},
	news        : {},
};

const actions = {
	searchLibrary({ commit }, query) {
		commit(LIBRARY_SEARCH, { query });
	},
	getLibrary({ commit }) {
		return new Promise((resolve, reject) => {
			commit(REQUEST_LIBRARY_LIST);

			api.get('library')
			   .then(response => {
				   commit(LIBRARY_LIST, {
					   games: response.data,
				   });

				   if (response.data.length > 0) {
					   for (let i = 0; i < response.data.length; i++) {
						   if (response.data[ i ].games.length > 0) {
							   return resolve(response.data[ i ].games[ 0 ]);
						   }
					   }
				   }

				   resolve(null);
			   })
			   .catch(error => {
				   commit(GET_LIBRARY_LIST_FAILURE, { error });
			   });
		});
	},
	getGameAchievements({ commit }, { gameID, userDataDir }) {
		return new Promise((resolve, reject) => {
			api.get(`game/${gameID}/achievements`)
			   .then(response => {
				   commit(ADD_LIBRARY_ACHIEVEMENTS, {
					   achievements: response.data,
					   gameID,
					   userDataDir,
				   });
				   resolve(response.data);
			   })
			   .catch(reject);
		});
	},
	getGameNews({ commit }, { gameID }) {
		return new Promise((resolve, reject) => {
			if (state.news.hasOwnProperty(gameID))
				return resolve(state.news[ gameID ]);

			api.get(`game/${gameID}/news`)
			   .then(response => {
				   commit(ADD_LIBRARY_NEWS, {
					   gameID,
					   news: response.data,
				   });
				   resolve(response.data);
			   })
			   .catch(reject);
		});
	},
	updateGameScreenshot({ commit }, { key, screenshots, gameID }) {
		commit(UPDATE_GAME_SCREENSHOT, { key, screenshots, gameID });
	},
	updateGameAchievementIcon({ commit }, { achievementID, resourcePath, isLocked }) {
		commit(UPDATE_GAME_ACHIEVEMENT, { achievementID, resourcePath, isLocked });
	},
	updateGameInstallState({ commit }, { games }) {
		commit(UPDATE_GAME_INSTALL_STATE, { games });
	},
};

const mutations = {
	[LIBRARY_SEARCH](state, action) {
		let query = action.query || '';

		if (query.trim().length <= 0) query = null;

		state.searchQuery = query;
	},
	[REQUEST_LIBRARY_LIST](state) {
		state.loading = true;
	},
	[LIBRARY_LIST](state, action) {
		state.loading = false;
		state.games   = action.games;
	},
	[GET_LIBRARY_LIST_FAILURE](state, action) {
		state.loading = false;
	},
	[ADD_LIBRARY_ACHIEVEMENTS](state, action) {
		if (!action.achievements) {
			if (!state.achievements.hasOwnProperty(action.gameID))
				state.achievements[ action.gameID ] = {};
			return;
		}

		const achievements = [];

		// Messy asf code ahead...
		Object.values(action.achievements.locked).forEach((l, k) => achievements.push({
			key  : Object.keys(action.achievements.locked)[ k ],
			value: l,
		}));
		Object.values(action.achievements.unlocked).forEach((u, k) => achievements.push({
			key  : Object.keys(action.achievements.unlocked)[ k ],
			value: u,
		}));

		if (achievements.length <= 0) return;

		achievements.forEach(a => {
			cache.grabImage(a.value.icon, 'achievements')
			     .then(data => {
				     action.achievements[ a.value.achieved ? 'unlocked' : 'locked' ][ a.key ].icon = data[ 0 ];
			     })
			     .catch(console.error);
		});

		state.achievements[ action.gameID ] = action.achievements;
	},
	[UPDATE_GAME_SCREENSHOT](state, action) {
		const game = library.findGameInCategories(state.games, action.gameID);
		if (!game) return;

		if (Array.isArray(action.screenshots)) {
			action.screenshots.forEach(shots => {
				game.screenshots[ shots.index ] = shots.resourcePath;
			});
		} else
			game.screenshots[ action.key ] = action.screenshots;
	},
	[UPDATE_GAME_ACHIEVEMENT](state, action) {
		const gameAchievements = state.achievements[ action.gameID ];
		if (!gameAchievements) return;

		let achievement = gameAchievements[ action.isLocked ? 'locked' : 'unlocked' ]
			.find(a => a.id === action.achievementID);

		if (!achievement) return;

		achievement.icon = action.resourcePath;
	},
	[UPDATE_GAME_INSTALL_STATE](state, action) {
		state.games.forEach(c => c.games.forEach(g => g.isInstalled = false));
		action.games.forEach(g => {
			const game = library.findGameInCategories(state.games, g.id);
			if (!game) return;
			game.isInstalled = true;
		});
	},
	[ADD_LIBRARY_NEWS](state, action) {
		if (!state.hasOwnProperty(action.gameID))
			state.news[ action.gameID ] = [];

		state.news[ action.gameID ] = action.news;
	},
};

export default {
	state,
	actions,
	mutations,
}


<|start_filename|>src/Store/types.js<|end_filename|>
export const GLOBAL_TOGGLE_SIDEBAR = 'GLOBAL_TOGGLE_SIDEBAR';

export const LIBRARY_SEARCH            = 'LIBRARY_SEARCH';
export const LIBRARY_LIST              = 'LIBRARY_LIST';
export const REQUEST_LIBRARY_LIST      = 'REQUEST_LIBRARY_LIST';
export const GET_LIBRARY_LIST_FAILURE  = 'GET_LIBRARY_LIST_FAILURE';
export const ADD_LIBRARY_ACHIEVEMENTS  = 'ADD_LIBRARY_ACHIEVEMENTS';
export const UPDATE_GAME_SCREENSHOT    = 'UPDATE_GAME_SCREENSHOT';
export const UPDATE_GAME_ACHIEVEMENT   = 'UPDATE_GAME_ACHIEVEMENT';
export const UPDATE_GAME_INSTALL_STATE = 'UPDATE_GAME_INSTALL_STATE';
export const ADD_LIBRARY_NEWS          = 'ADD_LIBRARY_NEWS';

export const USER_INFO             = 'USER_INFO';
export const REQUEST_USER_INFO     = 'REQUEST_USER_INFO';
export const GET_USER_INFO_FAILURE = 'GET_USER_INFO_FAILURE';


<|start_filename|>src/Store/utils/library.js<|end_filename|>
export default {
	findGameInCategories(categories, gameID) {
		if (typeof categories !== 'object' || isNaN(parseInt(gameID))) return null;

		if (typeof gameID === 'string')
			gameID = parseInt(gameID);

		for (let i = 0; i < categories.length; i++) {
			for (let c = 0; c < categories[ i ].games.length; c++) {
				if (categories[ i ].games[ c ].id === gameID)
					return categories[ i ].games[ c ];
			}
		}

		return null;
	},
}


<|start_filename|>src/Routes/app.js<|end_filename|>
import { Route, Router } from '../VueRouteHelper';

// Store

// Library
import Library from '../Views/Library.vue';
import LibraryGameView from '../Views/Partials/Library/GameView.vue';

// Community

// Settings

export default Router.add([
	// Store
	Route('/store', null).name('store'),

	Route('/library', Library).group(() => [
		Route(':appid', LibraryGameView).name('library.game'),
	]).name('library'),

	// Community
	Route('/community', null).name('community'),

	// Settings
	Route('/settings', null).name('settings'),

	// Default Redirect
	Route('*', null).redirect('/library'),
], true);


<|start_filename|>src/Routes/auth.js<|end_filename|>
import { Route, Router } from '../VueRouteHelper';

// Login
import LoginHome from '../Views/Partials/Auth/Login.vue';

// Steam Guard
import SteamGuard from '../Views/Partials/Auth/SteamGuard.vue';

// Register
import Register from '../Views/Partials/Auth/Register.vue';

export default Router.add([
	Route('/login', LoginHome).name('login'),

	Route('/guard', SteamGuard).name('login.guard'),

	Route('/register', Register).name('register'),

	Route('*', null).redirect('/login'),
], true);


<|start_filename|>src/Store/modules/global.js<|end_filename|>
import {
	GLOBAL_TOGGLE_SIDEBAR,
} from '../types';

const state = {
	sidebarCollapsed: false,
};

const actions = {
	toggleSidebar({ commit }, collapse = null) {
		commit(GLOBAL_TOGGLE_SIDEBAR, { collapse });
	},
};

const mutations = {
	[GLOBAL_TOGGLE_SIDEBAR](state, action) {
		state.sidebarCollapsed = action.collapse || !state.sidebarCollapsed;
	},
};

export default {
	state,
	actions,
	mutations,
}


<|start_filename|>src/Store/index.js<|end_filename|>
import Vue from 'vue';
import Vuex from 'vuex';
import createLogger from 'vuex/dist/logger';

// Modules
import global from './modules/global';
import library from './modules/library';
import user from './modules/user';

import * as actions from './actions';
import * as getters from './getters';

Vue.use(Vuex);

const debug = (process.env.NODE_ENV !== 'production');

export default new Vuex.Store({
	//actions,
	//getters,
	modules: {
		global,
		library,
		user,
	},
	strict : debug,
	plugins: (debug ? [ createLogger() ] : []),
})


<|start_filename|>src/Steam/index.js<|end_filename|>
const SteamUser    = require('steam-user');
const EventEmitter = require('events');
const fs           = require('fs');
const cache        = require('../cache');

const ChatManager = require('./chatManager');
const SteamCMD    = require('./steamCMD');

const DEFAULT_OPTIONS = {
	logOnDetails: {
		machineName: 'TecnoSteam-Client',
	},
};

class SteamClient extends EventEmitter {
	constructor(options = {}) {
		super();

		this.options = Object.assign({}, DEFAULT_OPTIONS, options);
		this.client  = new SteamUser({
			promptSteamGuardCode: false,
			enablePicsCache     : true,
		});

		this._chatManager = new ChatManager(this);
		this._steamCMD    = new SteamCMD();

		/*this._steamCMD.on('ready', () => {
			console.log('ready');
			this._steamCMD.appUpdate(252490);
		});*/

		this._steamGuardCallback = null;
		this._personaState       = 'Offline';

		this.bindEvents();
		this.readSettings();
	}

	autoLogin() {
		fs.access('login_key', fs.constants.F_OK, (err) => {
			if (err) {
				if (this.options.logonDetails && this.options.logonDetails.password)
					this.login();

				return;
			}

			fs.readFile('login_key', (err, data) => {
				if (err) throw err;
				this.login({
					logonDetails: {
						accountName: this.options.logonDetails.accountName || '[INSERT USERNAME HERE]', // MODIFY HERE
						loginKey   : data.toString(),
					},
				});
			});
		});
	}

	bindEvents() {
		this.client.on('loggedOn', (details, parental) => {
			this._steamGuardCallback = null;

			this.emit('loggedOn', arguments);
			this.emit('loginAttempt', false);

			// Without this we won't receive persona data about our friends
			this.personaState = SteamUser.EPersonaState.Online;
		});

		this.client.on('accountInfo', (name, country, authedMachines, flags, facebookID, facebookName) => {
			console.log(`Welcome, ${name} (${country})`);
		});

		this.client.on('friendsList', (...e) => this.emit('friendsList', ...e));

		this.client.on('error', (...e) => {
			// Check if someone is listening for errors
			// otherwise node.js throws it and the process exits
			// https://nodejs.org/api/events.html#events_error_events
			if (this.listenerCount('error') > 0)
				this.emit('error', ...e);

			fs.writeFile('error.log', JSON.stringify(...e), () => {
			});
		});

		this.client.on('disconnected', (result, message) => {
			this.emit('disconnected', arguments);

			/*if (this.options.autoReconnect)
				this.autoLogin();*/
		});

		this.client.on('steamGuard', (domain, callback) => {
			this._steamGuardCallback = ((code) => {
				this.emit('loginAttempt', true);
				callback(code);
			}).bind(this);

			this.emit('steamGuard', [
				domain,
				callback,
			]);
		});

		this.client.on('loginKey', (key) => fs.writeFile('login_key', key, () => {
		}));

		this.client.on('user', (sid, user) => {
			if (user.avatar_url_full) {
				// Cache user avatars
				cache.grabImage(user.avatar_url_full, 'users')
				     .then(data => this.getUser(sid).avatar_url_full = data[ 0 ])
				     .catch(console.error);
			}

			if (sid.accountid === this.client.steamID.accountid) {
				if (this.currentUser.persona_state !== user.persona_state)
					this._personaState = user.persona_state;
				this.emit('currentUser', user);
			}
		});

		this.client.on('friendRelationship', (sid, relationship) => {
			console.log(`${sid} relationship: ${SteamUser.EFriendRelationship[ relationship ]} (${relationship})`);
		});
	}

	readSettings() {
		try {
			fs.accessSync('settings', fs.constants.R_OK);

			const settings = JSON.parse(fs.readFileSync('settings').toString());

			Object.keys(settings).forEach(key => {
				switch (key) {
					case 'accountName':
						this.options.logOnDetails.accountName = settings[ key ];
						break;
					case 'rememberPassword':
						this.options.logOnDetails.rememberPassword = settings[ key ];
						break;
				}
			});
		} catch (e) {
			console.error(e);
		}
	}

	login(options) {
		this.emit('loginAttempt', true);

		options = Object.assign({}, this.options.logonDetails, options.logonDetails || options);

		if (options.hasOwnProperty('loginKey') && options.hasOwnProperty('password'))
			options.password = <PASSWORD>;
		if (options.hasOwnProperty('loginKey'))
			options.rememberPassword = true;

		const settings = {
			accountName     : options.accountName,
			rememberPassword: options.rememberPassword,
		};

		fs.writeFile('settings', JSON.stringify(settings), () => {
		});

		this.client.logOn(options);
	}

	logout() {
		this.client.logOff();
	}

	getUser(steamid) {
		return this.client.users[ steamid ];
	}

	// Getters
	get currentUser() {
		return (this.getUser(this.steamID64) || null);
	}

	get steamID64() {
		return this.client.steamID.getSteamID64();
	}

	get personaState() {
		if (!this.currentUser) return 'Offline';
		return SteamUser.EPersonaState[ this.currentUser.persona_state ];
	}

	get walletBalance() {
		if (!this.client.wallet.hasWallet) return 0;
		return SteamUser.formatCurrency(this.client.wallet.balance, this.client.wallet.currency);
	}

	get accountInfo() {
		return this.client.accountInfo;
	}

	get chatManager() {
		return this._chatManager;
	}

	get steamCMD() {
		return this._steamCMD;
	}

	get users() {
		return this.client.users;
	}

	// Setters
	set personaState(value) {
		if (typeof value === 'string')
			value = SteamUser.EPersonaState[ value ];
		this._personaState = SteamUser.EPersonaState[ value ];
		this.client.setPersona(value);
	}
}

module.exports = SteamClient;


<|start_filename|>src/Routes/friends.js<|end_filename|>
import { Route, Router } from '../VueRouteHelper';

import FriendsHome from '../Views/Partials/Friends/Home.vue';
import FriendsList from '../Views/Partials/Friends/Friends.vue';

export default Router.add([
	Route('/friends', FriendsHome)
		.group(() => [
			Route('/', FriendsList).name('friends.list'),
		]),

	Route('*', null).redirect('/friends'),
], true);


<|start_filename|>src/Store/modules/user.js<|end_filename|>
import api from '../utils/api';
import {
	USER_INFO,
	GET_USER_INFO_FAILURE,
	REQUEST_USER_INFO,
} from '../types';

const state = {
	loading : false,
	userInfo: {},
};

const actions = {
	getUserInfo({ commit }) {
		commit(REQUEST_USER_INFO);

		api.get('me')
		   .then(response => {
			   commit(USER_INFO, {
				   info: response.data,
			   });
		   })
		   .catch(error => {
			   commit(GET_USER_INFO_FAILURE, error);
		   });
	},
};

const mutations = {
	[REQUEST_USER_INFO](state) {
		state.loading = true;
	},
	[USER_INFO](state, action) {
		state.loading  = false;
		state.userInfo = action.info;
	},
	[GET_USER_INFO_FAILURE](state, action) {
		state.loading = false;
	},
};

export default {
	state,
	actions,
	mutations,
}


<|start_filename|>src/cache.js<|end_filename|>
const path     = require('path');
const url      = require('url');
const request  = require('request');
const fs       = require('fs');
const electron = require('electron');
let remote     = electron.app;

if (electron.remote)
    remote = electron.remote.app;

const cacheDirectory = path.join(remote.getPath('userData'), 'cache');

module.exports = {
    grabImage(uri, filePath, fileName = null, ...e) {
        return new Promise((resolve, reject) => {
            if (!uri) {
                if (filePath) {
                    return resolve([
                        this.unknownImage(filePath),
                        ...e,
                    ])
                }

                return reject('\'uri\' cannot be null!');
            }
            if (!filePath) return reject('\'filePath\' cannot be null!');

            if (fileName === null)
                fileName = path.basename(url.parse(uri).pathname);

            if (fileName === null)
                return reject('\'fileName\' cannot be null!');

            const cacheFolderName = filePath;

            filePath = path.join(path.join(cacheDirectory, filePath), fileName);

            let response = filePath;

            const resourcePath = '/dist/' + path.join('cache', cacheFolderName,
                path.basename(response)).replace(/\\/g, '\/');

            if (e.length > 0)
                response = [ response, ...e ];

            if (Array.isArray(response))
                response = [ resourcePath, ...response ];
            else
                response = [ resourcePath, response ];

            if (fs.existsSync(filePath))
                return resolve(response);

            request(uri).pipe(fs.createWriteStream(filePath))
                        .on('close', () => resolve(response));
        });
    },
    unknownImage(type) {
        switch (type.toLowerCase()) {
            case 'achievements':
            case 'achievement':
                return '/dist/imgs/unknown-achievement.png';
                break;
        }
    },
};


<|start_filename|>src/Routes/chat.js<|end_filename|>
import { Route, Router } from '../VueRouteHelper';

// Login
import MessageHome from '../Views/Partials/Chat/Home.vue';
import MessageChat from '../Views/Partials/Chat/Chat.vue';

export default Router.add([
	Route('/', MessageHome).group(() => [
		Route(':roomid', MessageChat).name('chat.view'),
	]),

	Route('*', null).redirect('/'),
], true);


<|start_filename|>src/Directives/tooltip.js<|end_filename|>
import Vue from 'vue';

Vue.directive('tooltip', {
	inserted(e, binding, vnode) {
		const data = binding.value;
		const $tt  = vnode.context.$root.app.tooltip;

		$(e).hover(function () {
			$tt.setData(data)
			   .show({
				   element: this,
				   offset : data.position,
			   });
		}, function () {
			$tt.hide();
		});
	},
});


<|start_filename|>src/Directives/index.js<|end_filename|>
require('./tooltip');
