diff --git a/.install/.config/autostart/Astroport_X_config.desktop b/.install/.config/autostart/Astroport_X_config.desktop new file mode 100644 index 0000000..6c86468 --- /dev/null +++ b/.install/.config/autostart/Astroport_X_config.desktop @@ -0,0 +1,9 @@ +[Desktop Entry] +Type=Application +Exec=~/.zen/astroport/1stRUNconfig.sh +X-GNOME-Autostart-enabled=true +NoDisplay=false +Hidden=false +Name[fr_FR]=Astroport_X_config.desktop +Comment[fr_FR]=First time RUN config script +X-GNOME-Autostart-Delay=0 diff --git a/.install/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py b/.install/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py new file mode 100644 index 0000000..b22b518 --- /dev/null +++ b/.install/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py @@ -0,0 +1,1703 @@ +# -*- coding: utf-8 -*- +# vStream https://github.com/Kodi-vStream/venom-xbmc-addons +from resources.lib.comaddon import progress, addon, dialog +from resources.lib.gui.gui import cGui +from resources.lib.gui.guiElement import cGuiElement +from resources.lib.gui.hoster import cHosterGui +from resources.lib.handler.inputParameterHandler import cInputParameterHandler +from resources.lib.handler.outputParameterHandler import cOutputParameterHandler +from resources.lib.handler.requestHandler import cRequestHandler +from resources.lib.tmdb import cTMDb +from resources.lib.util import Quote, cUtil, Unquote + +import random + +SITE_IDENTIFIER = 'astroport' +SITE_NAME = 'ASTROPORT _PROFIL_ (_LOGIN_) (_MDP_)' +SITE_DESC = 'Partage ~/astroport/films (animes, series) avec ses amis https://cesium.app via IPFS' + +URL_MAIN = 'http://localhost:8080/ipns/_IPNSKEY_/xbian/' + +KEY_PASTE_ID = 'PASTE_ID' +SETTING_PASTE_ID = 'pastebin_id_' +SETTING_PASTE_LABEL = 'pastebin_label_' +UNCLASSIFIED_GENRE = '_NON CLASSÉ_' +UNCLASSIFIED = 'Indéterminé' + +URL_SEARCH_MOVIES = (URL_MAIN + '&pasteID=' + KEY_PASTE_ID + '&sMedia=film&sSearch=', 'showSearchGlobal') +URL_SEARCH_SERIES = (URL_MAIN + '&pasteID=' + KEY_PASTE_ID + '&sMedia=serie&sSearch=', 'showSearchGlobal') +URL_SEARCH_ANIMS = (URL_MAIN + '&pasteID=' + KEY_PASTE_ID + '&sMedia=anime&sSearch=', 'showSearchGlobal') +FUNCTION_SEARCH = 'showSearchGlobal' + + +def getNbItemParPage(): + nbItem = addon().getSetting('pastebin_nbItemParPage') + if nbItem: + return int(nbItem) + return 25 + +ITEM_PAR_PAGE = getNbItemParPage() +PASTE_PAR_GROUPE = 50 # jusqu'à 50 groupes de paste, chaque groupe peut contenir jusqu'à 50 liens pastebin + +# Exemple +# CAT; TMDB; TITLE; SAISON; YEAR; GENRES; URLS=https://uptobox.com/ +# film;714;Demain ne meurt jamais;James BOND;1997;['Action', 'Aventure', 'Thriller'];['nwxxxx','nwYYzz'] +# serie;48866;Les 100;Saison 2; 2014; ['Fantastique', 'Aventure']; {'S02E01':['lien1', 'lien2'], 'S02E02':['lien1']} + +# Exemple minimum +# TITLE; URLS +# Demain ne meurt jamais;['https://uptobox.com/nwxxxx'] + +class PasteBinContent: + CAT = -1 # (Optionnel) - Catégorie 'film', 'serie' 'anime' (Film par défaut) + TMDB = -1 # (optionnel) - Id TMDB + TITLE = -1 # Titre du film / épisodes + SAISON = -1 # (optionnel) - Saison pour les séries (ex 'Saison 03' ou 'S03' ou '03') OU Saga pour les films (ex 'Mission impossible') + GROUPES = -1 # (optionnel) - Groupes tel que NETFLIX, HBO, MARVEL, DISNEY, Films enfants, ... + YEAR = -1 # (optionnel) - Année + GENRES = -1 # (optionnel) - Liste des genres + RES = -1 # (optionnel) - Résolution (720p, 1080p, 4K, ...) + DIRECTOR = -1# (optionnel) - Réalisateur au format id:nom + CAST = -1 # (optionnel) - Acteurs au format id:nom + NETWORK = -1 # (optionnel) - Diffuseur au format id:nom + URLS = -1 # Liste des liens, avec épisodes pour les séries + HEBERGEUR = '' # (optionnel) - URL de l'hebergeur, pour éviter de le mettre dans chaque URL, ex : 'https://uptobox.com/' + + # Pour comparer deux pastes, savoir si les champs sont dans le même ordre + def isFormat(self, other): + if not isinstance(other, PasteBinContent): + return False + + return self.CAT == other.CAT \ + and self.TMDB == other.TMDB \ + and self.TITLE == other.TITLE \ + and self.SAISON == other.SAISON \ + and self.GROUPES == other.GROUPES \ + and self.YEAR == other.YEAR \ + and self.GENRES == other.GENRES \ + and self.RES == other.RES \ + and self.DIRECTOR == other.DIRECTOR \ + and self.CAST == other.CAST \ + and self.NETWORK == other.NETWORK \ + and self.URLS == other.URLS + + def getLines(self, sContent): + lines = sContent.splitlines() + + # Vérifie si la ligne d'entete existe avec les champs obligatoires + entete = lines[0].split(";") + if 'TITLE' not in entete and 'URLS' not in entete: + return [] + + # Calcul des index de chaque champ + idx = 0 + for champ in entete: + champ = champ.strip() + + if 'URL' in champ: # supporte URL ou URLS + hebergeur = champ.split('=') + champ = 'URLS' + if len(hebergeur)>1: + self.HEBERGEUR = hebergeur[1].replace(' ','').replace('"','').replace('\'','') + if not self.HEBERGEUR.endswith('/'): + self.HEBERGEUR += '/' + if champ in dir(self): + setattr(self, champ, idx) + idx +=1 + + lines = [k.split(";") for k in lines[1:]] + + + # Reconstruire les liens + if self.HEBERGEUR: + for line in lines: + sHost = line[self.URLS] + if "{" in sHost: + sUrl = eval(sHost) + for link in sUrl.keys(): + sUrl[link] = self.HEBERGEUR + sUrl[link] + elif "[" in sHost: + sHost = eval(sHost) + sUrl = [(self.HEBERGEUR + link) for link in sHost] + else: + sUrl = self.HEBERGEUR + sHost + line[self.URLS] = sUrl + + return lines + + +def load(): + addons = addon() + oGui = cGui() + + numID = 0 + pasteListe = {} + + # Recherche des listes déclarées + for numID in range(1, 50): + pasteLabel = addons.getSetting(SETTING_PASTE_LABEL + str(numID)) + if pasteLabel: + pasteListe[pasteLabel] = numID + + # Trie des listes par label + pasteListe = sorted(pasteListe.items(), key=lambda paste: paste[0]) + + if len(pasteListe)>0: + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_MOVIES[0] + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Films)', 'search.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_SERIES[0] + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Séries)', 'search.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_ANIMS[0] + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Animes)', 'search.png', oOutputParameterHandler) + + + for pasteBin in pasteListe: + pasteLabel = pasteBin[0] + pasteID = pasteBin[1] + + oGuiElement = cGuiElement() + oGuiElement.setSiteName(SITE_IDENTIFIER) + oGuiElement.setFunction('showMenu') + oGuiElement.setTitle(pasteLabel) + oGuiElement.setIcon("mark.png") + oGuiElement.setMeta(0) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('pasteID', pasteID) + oGui.CreateSimpleMenu(oGuiElement, oOutputParameterHandler, SITE_IDENTIFIER, SITE_IDENTIFIER, 'deletePasteName', addons.VSlang(30412)) + oGui.addFolder(oGuiElement, oOutputParameterHandler) + + + # Menu pour ajouter un lien + oOutputParameterHandler = cOutputParameterHandler() + oGui.addDir(SITE_IDENTIFIER, 'addPasteName', '[COLOR coral]Ajouter un dossier PasteBin[/COLOR]', 'listes.png', oOutputParameterHandler) + + + oGui.setEndOfDirectory() + + +def showMenu(): + addons = addon() + oGui = cGui() + + oInputParameterHandler = cInputParameterHandler() + pasteID = oInputParameterHandler.getValue('pasteID') + sMedia = oInputParameterHandler.getValue('sMedia') + + prefixID = SETTING_PASTE_ID + str(pasteID) + pasteBin = addons.getSetting(prefixID) + contenu = getPasteBin(pasteBin) + + for numID in range(1, PASTE_PAR_GROUPE): + pasteBin = addons.getSetting(prefixID + '_' + str(numID)) + contenu = contenu.union(getPasteBin(pasteBin)) + + if not sMedia: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('pasteID', pasteID) + + cnt = contenu.intersection(['containFilms', 'containSeries', 'containAnimes']) + if len(cnt) == 1: + showDetailMenu(pasteID, contenu) + else: + if 'containFilms' in contenu: + oOutputParameterHandler.addParameter('sMedia', 'film') + oGui.addDir(SITE_IDENTIFIER, 'showMenu', 'Films', 'films.png', oOutputParameterHandler) + + if 'containSeries' in contenu: + oOutputParameterHandler.addParameter('sMedia', 'serie') + oGui.addDir(SITE_IDENTIFIER, 'showMenu', 'Séries', 'tv.png', oOutputParameterHandler) + + if 'containAnimes' in contenu: + oOutputParameterHandler.addParameter('sMedia', 'anime') + oGui.addDir(SITE_IDENTIFIER, 'showMenu', 'Animes', 'animes.png', oOutputParameterHandler) + + oGui.addDir(SITE_IDENTIFIER, 'addPasteID', '[COLOR coral]Ajouter un lien PasteBin[/COLOR]', 'listes.png', oOutputParameterHandler) + + elif 'film' in sMedia: + contenu.discard('containSeries') + contenu.discard('containAnimes') + showDetailMenu(pasteID, contenu) + elif 'serie' in sMedia: + contenu.discard('containFilms') + contenu.discard('containAnimes') + showDetailMenu(pasteID, contenu) + elif 'anime' in sMedia: + contenu.discard('containFilms') + contenu.discard('containSeries') + showDetailMenu(pasteID, contenu) + + oGui.setEndOfDirectory() + + + +def showDetailMenu(pasteID, contenu): + oGui = cGui() + + sUrl = URL_MAIN #+ pasteBin + if 'containFilms' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_MOVIES[0].replace(KEY_PASTE_ID, pasteID) + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Films)', 'search.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', 'Films (Derniers ajouts)', 'news.png', oOutputParameterHandler) + + if 'containFilmGenres' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGenres', 'Films (Genres)', 'genres.png', oOutputParameterHandler) + + if 'containFilmGroupes' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGroupes', 'Films (Listes)', 'genres.png', oOutputParameterHandler) + + if 'containFilmSaga' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showSaga', 'Films (Saga)', 'genres.png', oOutputParameterHandler) + + if 'containFilmYear' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showYears', 'Films (Par années)', 'annees.png', oOutputParameterHandler) + + if 'containFilmRes' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showResolution', 'Films (Par résolutions)', 'hd.png', oOutputParameterHandler) + + if 'containFilmNetwork' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showNetwork', 'Films (Par diffuseurs)', 'host.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'AlphaList', 'Films (Ordre alphabétique)', 'az.png', oOutputParameterHandler) + + if 'containFilmReal' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showRealisateur', 'Films (Par réalisateurs)', 'actor.png', oOutputParameterHandler) + + if 'containFilmCast' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showCast', 'Films (Par acteurs)', 'actor.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=film&bRandom=True&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', 'Films (Aléatoires)', 'news.png', oOutputParameterHandler) + + + if 'containSeries' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_SERIES[0].replace(KEY_PASTE_ID, pasteID) + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Séries)', 'search.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', 'Séries (Derniers ajouts)', 'news.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGenres', 'Séries (Genres)', 'genres.png', oOutputParameterHandler) + + if 'containSerieGroupes' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGroupes', 'Séries (Listes)', 'genres.png', oOutputParameterHandler) + + if 'containSerieYear' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showYears', 'Séries (Par années)', 'annees.png', oOutputParameterHandler) + + if 'containSerieNetwork' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showNetwork', 'Séries (Par diffuseurs)', 'genres.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=serie&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'AlphaList', 'Séries (Ordre alphabétique)', 'az.png', oOutputParameterHandler) + + + if 'containAnimes' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + searchUrl = URL_SEARCH_ANIMS[0].replace(KEY_PASTE_ID, pasteID) + oOutputParameterHandler.addParameter('siteUrl', searchUrl) + oGui.addDir(SITE_IDENTIFIER, 'showSearch', 'Recherche (Animes)', 'search.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=anime&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', 'Animes (Derniers ajouts)', 'news.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=anime&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGenres', 'Animes (Genres)', 'genres.png', oOutputParameterHandler) + + if 'containAnimeGroupes' in contenu: + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=anime&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'showGroupes', 'Animes (Listes)', 'genres.png', oOutputParameterHandler) + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl + '&sMedia=anime&pasteID=' + pasteID) + oGui.addDir(SITE_IDENTIFIER, 'AlphaList', 'Animes (Ordre alphabétique)', 'listes.png', oOutputParameterHandler) + + + +def getPasteBin(pasteBin): + + containList = set() + + if not pasteBin: + return containList + + # Etablir les menus en fonction du contenu + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + sContent = oRequestHandler.request() + + pbContent = PasteBinContent() + movies = pbContent.getLines(sContent) + + # Calculer les menus + for movie in movies: + if pbContent.CAT == -1 or 'film' in movie[pbContent.CAT]: + containList.add('containFilms') + if pbContent.GENRES>=0 and len(movie[pbContent.GENRES].strip())>0: + containList.add('containFilmGenres') + if pbContent.GROUPES>=0 and len(movie[pbContent.GROUPES].replace('[', '').replace(']', '').strip())>0: + containList.add('containFilmGroupes') + if pbContent.YEAR>=0 and len(movie[pbContent.YEAR].strip())>0: + containList.add('containFilmYear') + if pbContent.RES>=0 and len(movie[pbContent.RES].replace('[', '').replace(']', '').replace(',', '').strip())>0: + containList.add('containFilmRes') + if pbContent.DIRECTOR>=0 and len(movie[pbContent.DIRECTOR].replace('[', '').replace(']', '').replace(',', '').strip())>0: + containList.add('containFilmReal') + if pbContent.CAST>=0 and len(movie[pbContent.CAST].replace('[', '').replace(']', '').replace(',', '').strip())>0: + containList.add('containFilmCast') + if pbContent.SAISON>=0 and len(movie[pbContent.SAISON].strip())>0: + containList.add('containFilmSaga') + if pbContent.NETWORK>=0 and len(movie[pbContent.NETWORK].replace('[', '').replace(']', '').strip())>0: + containList.add('containFilmNetwork') + + elif 'serie' in movie[pbContent.CAT]: + containList.add('containSeries') + if pbContent.GROUPES>=0 and len(movie[pbContent.GROUPES].replace('[', '').replace(']', '').strip())>0: + containList.add('containSerieGroupes') + if pbContent.NETWORK>=0 and len(movie[pbContent.NETWORK].replace('[', '').replace(']', '').strip())>0: + containList.add('containSerieNetwork') + if pbContent.YEAR>=0 and len(movie[pbContent.YEAR].strip())>0: + containList.add('containSerieYear') + + elif 'anime' in movie[pbContent.CAT]: + containList.add('containAnimes') + if pbContent.GROUPES>=0 and len(movie[pbContent.GROUPES].replace('[', '').replace(']', '').strip())>0: + containList.add('containAnimeGroupes') + return containList + + +def showSearch(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + sUrl = oInputParameterHandler.getValue('siteUrl') + + sSearchText = oGui.showKeyBoard() + if (sSearchText != False): + sUrl += Quote(sSearchText) + + # Recherche globale si le pastebin n'est pas indiqué + if KEY_PASTE_ID in sUrl: + showSearchGlobal(sUrl) + else: + showMovies(sUrl) + oGui.setEndOfDirectory() + + +def showSearchGlobal(sSearch=''): + addons = addon() + + sUrl = sSearch + + for numID in range(1, PASTE_PAR_GROUPE): + prefixID = SETTING_PASTE_LABEL + str(numID) + pastebin = addons.getSetting(prefixID) + if pastebin: + searchUrl = sUrl.replace(KEY_PASTE_ID, str(numID)) + try: + showMovies(searchUrl) + except: + pass + + +def showGenres(): + tmdb = cTMDb() + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + listeIDs = getPasteList(sUrl, pasteID) + + genres = {} + for pasteBin in listeIDs: + + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + sContent = oRequestHandler.request() + pbContent = PasteBinContent() + movies = pbContent.getLines(sContent) + + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + genre = movie[pbContent.GENRES].strip() + if not genre or genre == '': + genre = "['"+UNCLASSIFIED_GENRE+"']" + elif "''" in genre: + genre = genre.replace("''", "'"+UNCLASSIFIED_GENRE+"'") + genre = eval(genre) + if isinstance(genre, int): + genre = [genre] + if genre: + for g in genre: + sDisplayGenre = g + if str(g).isdigit(): + sDisplayGenre = tmdb.getGenreFromID(g) + if not sDisplayGenre in genres: + genres[sDisplayGenre] = g + + genreKeys = genres.keys() + for sDisplayGenre in sorted(genreKeys): + genre = genres.get(sDisplayGenre) + oOutputParameterHandler = cOutputParameterHandler() + sUrl = siteUrl + '&sGenre=' + str(genre) + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', sDisplayGenre, 'genres.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + +def showNetwork(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + listNetwork = {} + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + networks = movie[pbContent.NETWORK].strip() + if networks <> '': + networks = eval(networks) + if networks: + for network in networks: + if ':' in network: + networkId, networkName = network.split(':') + if networkName not in listNetwork: + listNetwork[networkName] = networkId + + maxProgress = len(listNetwork) + progress_ = progress().VScreate(SITE_NAME) + + for networkName, networkId in sorted(listNetwork.items()): + progress_.VSupdate(progress_, maxProgress) + if progress_.iscanceled(): + break + + sUrl = siteUrl + '&sNetwork=' + networkId + ":" + networkName.replace('+', '|') + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sTmdbId', networkId) # Utilisé par TMDB + oGui.addNetwork(SITE_IDENTIFIER, 'showMovies', networkName, 'host.png', oOutputParameterHandler) + progress_.VSclose(progress_) + + oGui.setEndOfDirectory() + +def showRealisateur(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + numItem = oInputParameterHandler.getValue('numItem') + numPage = oInputParameterHandler.getValue('numPage') + + # Gestion de la pagination + if not numItem: + numItem = 0 + numPage = 1 + numItem = int(numItem) + numPage = int(numPage) + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + listReal = {} + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + reals = movie[pbContent.DIRECTOR].strip() + if reals <> '': + reals = eval(reals) + if reals: + for real in reals: + if ':' in real: + realId, realName = real.split(':') + if realName not in listReal: + listReal[realName] = realId + + nbItem = 0 + index = 0 + maxProgress = min(len(listReal), ITEM_PAR_PAGE) + progress_ = progress().VScreate(SITE_NAME) + + for realName, realId in sorted(listReal.items()): + # Pagination, on se repositionne + index += 1 + if index <= numItem: + continue + numItem += 1 + + progress_.VSupdate(progress_, maxProgress) + if progress_.iscanceled(): + break + + sUrl = siteUrl + '&sDirector=' + realId + ":" + realName + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sTmdbId', realId) # Utilisé par TMDB + oGui.addPerson(SITE_IDENTIFIER, 'showMovies', realName, 'actor.png', oOutputParameterHandler) + + nbItem += 1 + if nbItem % ITEM_PAR_PAGE == 0 and numPage*ITEM_PAR_PAGE < len(listReal) : + numPage += 1 + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', siteUrl) + oOutputParameterHandler.addParameter('numPage', numPage) + oOutputParameterHandler.addParameter('numItem', numItem) + oGui.addNext(SITE_IDENTIFIER, 'showRealisateur', '[COLOR teal]Page ' + str(numPage) + ' >>>[/COLOR]', oOutputParameterHandler) + break + + progress_.VSclose(progress_) + + oGui.setEndOfDirectory() + +def showCast(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + numItem = oInputParameterHandler.getValue('numItem') + numPage = oInputParameterHandler.getValue('numPage') + + # Gestion de la pagination + if not numItem: + numItem = 0 + numPage = 1 + numItem = int(numItem) + numPage = int(numPage) + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + listActeur = {} + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + acteurs = movie[pbContent.CAST].strip() + if acteurs <> '': + acteurs = eval(acteurs) + if acteurs: + for acteur in acteurs: + if ':' in acteur: + acteurId, acteurName = acteur.split(':') + if acteurName not in listActeur: + listActeur[acteurName] = acteurId + + nbItem = 0 + index = 0 + maxProgress = min(len(listActeur), ITEM_PAR_PAGE) + progress_ = progress().VScreate(SITE_NAME) + + for acteurName, acteurId in sorted(listActeur.items()): + # Pagination, on se repositionne + index += 1 + if index <= numItem: + continue + numItem += 1 + + progress_.VSupdate(progress_, maxProgress) + if progress_.iscanceled(): + break + + sUrl = siteUrl + '&sCast=' + acteurId + ":" + acteurName + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sTmdbId', acteurId) # Utilisé par TMDB + oGui.addPerson(SITE_IDENTIFIER, 'showMovies', acteurName, 'actor.png', oOutputParameterHandler) + + nbItem += 1 + if nbItem % ITEM_PAR_PAGE == 0 and numPage*ITEM_PAR_PAGE < len(listActeur) : + numPage += 1 + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', siteUrl) + oOutputParameterHandler.addParameter('numPage', numPage) + oOutputParameterHandler.addParameter('numItem', numItem) + oGui.addNext(SITE_IDENTIFIER, 'showCast', '[COLOR teal]Page ' + str(numPage) + ' >>>[/COLOR]', oOutputParameterHandler) + break + + progress_.VSclose(progress_) + + oGui.setEndOfDirectory() + +def showGroupes(): + oGui = cGui() + + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + sousGroupe = set() + groupesPerso = set() + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + groupe = movie[pbContent.GROUPES].strip().replace("''",'') + if groupe: + groupe = eval(groupe) + if groupe: + for gr in groupe: + if ':' in gr: + grID = gr.split(':')[0] + if grID not in sousGroupe: + sousGroupe.add(grID) + else: + groupesPerso.add(gr) + + groupes = groupesPerso.union(sousGroupe) + for sGroupe in sorted(groupes): + sUrl = siteUrl + '&sGroupe=' + sGroupe + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + if sGroupe in sousGroupe: + oGui.addDir(SITE_IDENTIFIER, 'showGroupeDetails', sGroupe, 'genres.png', oOutputParameterHandler) + else: + oGui.addDir(SITE_IDENTIFIER, 'showMovies', sGroupe, 'genres.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + +def showGroupeDetails(): + oGui = cGui() + + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + sGroupe = aParams['sGroupe'] + ':' if 'sGroupe' in aParams else None + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + groupes = set() + if sGroupe: + for movie in movies: + groupe = movie[pbContent.GROUPES].strip().replace("''",'') + if groupe: + groupe = eval(groupe) + if groupe: + for gr in groupe: + if gr.startswith(sGroupe): + groupes.add(gr) + + for sGroupe in sorted(groupes): + sUrl = siteUrl + '&sGroupe=' + sGroupe.replace('+', '|') + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + sDisplayGroupe = sGroupe.split(':')[1] + oGui.addDir(SITE_IDENTIFIER, 'showMovies', sDisplayGroupe, 'genres.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + +def showSaga(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + numItem = oInputParameterHandler.getValue('numItem') + numPage = oInputParameterHandler.getValue('numPage') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + if not numItem: + numItem = 0 + numPage = 1 + numItem = int(numItem) + numPage = int(numPage) + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + sagas = {} + for movie in movies: + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + saga = movie[pbContent.SAISON].strip() + if saga <> '': + sTmdbId = name = saga + idName = saga.split(':', 1) + if len(idName)>1: + sTmdbId = idName[0] + name = idName[1] + if sTmdbId.isdigit(): + sagas[name] = sTmdbId + else: + sagas[saga] = saga + + nbItem = 0 + index = 0 + progress_ = progress().VScreate(SITE_NAME) + names = sagas.keys() + for sSagaName in sorted(names): + + # Pagination, on se repositionne + index += 1 + if index <= numItem: + continue + numItem += 1 + + progress_.VSupdate(progress_, ITEM_PAR_PAGE) + if progress_.iscanceled(): + break + + sTmdbId = sagas[sSagaName] + + oOutputParameterHandler = cOutputParameterHandler() + if sTmdbId.isdigit(): + oOutputParameterHandler.addParameter('sTmdbId', sTmdbId) # Utilisé par TMDB + sUrl = siteUrl + '&sSaga=' + sTmdbId + ':' + sSagaName + else: + sUrl = siteUrl + '&sSaga=' + sSagaName + oOutputParameterHandler.addParameter('siteUrl', sUrl) + + sDisplaySaga = sSagaName + sSagaName = sSagaName.replace('[', '').replace(']', '') # Exemple pour le film [REC], les crochets sont génant pour certaines fonctions + if not sSagaName.lower().endswith('saga'): + sSagaName = sSagaName + " Saga" + oOutputParameterHandler.addParameter('sMovieTitle', sSagaName) + + oGui.addMoviePack(SITE_IDENTIFIER, 'showMovies', sDisplaySaga, 'genres.png', '', '', oOutputParameterHandler) + + nbItem += 1 + if nbItem % ITEM_PAR_PAGE == 0 and numPage*ITEM_PAR_PAGE < len(names) : + numPage += 1 + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', siteUrl) + oOutputParameterHandler.addParameter('numPage', numPage) + oOutputParameterHandler.addParameter('numItem', numItem) + oGui.addNext(SITE_IDENTIFIER, 'showSaga', '[COLOR teal]Page ' + str(numPage) + ' >>>[/COLOR]', oOutputParameterHandler) + break + + + progress_.VSclose(progress_) + + oGui.setEndOfDirectory() + + +def showYears(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + years = set() + for line in movies: + if pbContent.CAT >=0 and sMedia not in line[pbContent.CAT]: + continue + + year = line[pbContent.YEAR].strip() + if not year: + year = UNCLASSIFIED + years.add(year) + + for sYear in sorted(years, reverse=True): + sUrl = siteUrl + '&sYear=' + sYear + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', sYear, 'years.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + + +def showResolution(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + sUrl, params = siteUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + resolutions = set() + for line in movies: + if pbContent.CAT >=0 and sMedia not in line[pbContent.CAT]: + continue + + res = line[pbContent.RES].strip() + if '[' in res: + if res != '[]': + res = eval(res) + resolutions = resolutions.union(res) + if '' in res or len(res) == 0: + resolutions.add(UNCLASSIFIED) + else: + resolutions.add(res) + + if not res or res == '[]' : resolutions.add(UNCLASSIFIED) + + resolutions.discard('') + + # Trie des rsolutions + resOrder = ['8K','4K','1080P', '1080p', '720P', '720p', '576p', '540P', '540p', '480P', '480p', '360P', '360p'] + def trie_res(key): + if key == UNCLASSIFIED: + return 20 + if key not in resOrder: + resOrder.append(key) + return resOrder.index(key) + + resolutions = sorted(resolutions, key=trie_res) + + for sRes in resolutions: + if sRes == '': continue + + sDisplayRes = sRes + if sDisplayRes.isdigit(): sDisplayRes += 'p' + sDisplayRes = sDisplayRes\ + .replace('P', 'p')\ + .replace('1080p', 'HD [1080p]')\ + .replace('720p', 'HD [720p]')\ + .replace('540p', 'SD [540p]')\ + .replace('480p', 'SD [480p]')\ + .replace('360p', 'SD [360p]')\ + .replace('4K', '2160p')\ + .replace('8K', '4320p')\ + .replace('2160p', '4K [2160p]')\ + .replace('4320p', '8K [4320p]') + + sUrl = siteUrl + '&sRes=' + sRes + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', sDisplayRes, 'hd.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + + +def AlphaList(): + oGui = cGui() + + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + for i in range(0, 36): + if (i < 10): + sLetter = chr(48 + i) + else: + sLetter = chr(65 + i -10) + + sUrl = siteUrl + '&sAlpha=' + sLetter + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oGui.addDir(SITE_IDENTIFIER, 'showMovies', '[COLOR teal] Lettre [COLOR red]' + sLetter + '[/COLOR][/COLOR]', 'listes.png', oOutputParameterHandler) + + oGui.setEndOfDirectory() + + +def showMovies(sSearch=''): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + numItem = oInputParameterHandler.getValue('numItem') + numPage = oInputParameterHandler.getValue('numPage') + sMedia = 'film' # Par défaut + pasteID = sGenre = sSaga = sGroupe = sYear = sRes = sAlpha = sNetwork = sDirector = sCast = None + bRandom = False + + if sSearch: + siteUrl = sSearch + + if not numItem: + numItem = 0 + numPage = 1 + numItem = int(numItem) + numPage = int(numPage) + + sSearchTitle = '' + + # Pour supporter les caracteres '&' et '+' dans les noms alors qu'ils sont réservés + sUrl = siteUrl.replace('+', ' ').replace('|', '+').replace(' & ', ' | ') + + sUrl, params = sUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + + if 'pasteID' in aParams: pasteID = aParams['pasteID'] + if 'sMedia' in aParams: sMedia = aParams['sMedia'] + if 'sSearch' in aParams: sSearchTitle = Unquote(aParams['sSearch']).replace(' | ', ' & ') + if 'sGenre' in aParams: sGenre = aParams['sGenre'].replace(' | ', ' & ') + if 'sSaga' in aParams: + sSaga = aParams['sSaga'].replace(' | ', ' & ') + if 'sGroupe' in aParams: sGroupe = aParams['sGroupe'].replace(' | ', ' & ') + if 'sYear' in aParams: sYear = aParams['sYear'] + if 'sRes' in aParams: sRes = aParams['sRes'] + if 'sAlpha' in aParams: sAlpha = aParams['sAlpha'] + if 'sNetwork' in aParams: sNetwork = aParams['sNetwork'] + if 'sDirector' in aParams: sDirector = aParams['sDirector'] + if 'sCast' in aParams: sCast = aParams['sCast'] + if 'bRandom' in aParams: bRandom = aParams['bRandom'] + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(sUrl, pasteID) + for pasteBin in listeIDs: + oRequestHandler = cRequestHandler(URL_MAIN + pasteBin) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + # Recherche par ordre alphabetique => le tableau doit être trié + if sAlpha: + movies = sorted(movies, key=lambda line: line[pbContent.TITLE]) + + # Recherche par saga => trie par années + if sSaga and pbContent.YEAR>=0: + movies = sorted(movies, key=lambda line: line[pbContent.YEAR]) + + # Dans un dossier => trie par années inversées (du plus récent) + if sGroupe or sDirector or sCast: + movies = sorted(movies, key=lambda line: line[pbContent.YEAR], reverse=True) + + if bRandom: + numItem = 0 + # Génération d'indices aléatoires, ajout de deux indices car les doublons aléatoires sont rassemblés + randoms = [random.randint(0, len(movies)) for _ in range(ITEM_PAR_PAGE+2)] + + movieIds = set() + + nbItem = 0 + index = 0 + progress_ = progress().VScreate(SITE_NAME) + + for movie in movies: + + if bRandom and index not in randoms: + index += 1 + continue + + # Pagination, on se repositionne + index += 1 + if index <= numItem: + continue + numItem += 1 + + # Filtrage par média (film/série), "film" par défaut si pas précisé + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + # Filtrage par saga + if sSaga and sSaga != movie[pbContent.SAISON].strip(): + continue + + # Filtrage par genre + if sGenre and pbContent.GENRES >=0 : + genres = movie[pbContent.GENRES].strip() + if not genres or genres == '' or "''" in genres: + if sGenre != UNCLASSIFIED_GENRE: + continue + elif genres: + genres = eval(genres) + genres = [str(g) for g in genres] + if sGenre not in genres: + continue + + # Filtrage par réalisateur + if sDirector and pbContent.DIRECTOR >= 0 : + listDirector = movie[pbContent.DIRECTOR].strip() + if not listDirector: + continue + listDirector = eval(listDirector) + if sDirector not in listDirector: + continue + + # Filtrage par acteur + if sCast and pbContent.CAST >=0 : + listCast = movie[pbContent.CAST].strip() + if not listCast: + continue + listCast = eval(listCast) + if sCast not in listCast: + continue + + # Filtrage par diffuseur + if sNetwork and pbContent.NETWORK >= 0 : + listNetwork = movie[pbContent.NETWORK].strip() + if not listNetwork: + continue + listNetwork = eval(listNetwork) + if sNetwork not in listNetwork: + continue + + # Filtrage par groupe + if sGroupe and pbContent.GROUPES >= 0: + groupes = movie[pbContent.GROUPES].strip() + if not groupes: + continue + groupes = eval(groupes) + if sGroupe not in groupes: + continue + + # l'ID TMDB + sTmdbId = None + if pbContent.TMDB >= 0: + sTmdbId = movie[pbContent.TMDB].strip() + if sTmdbId in movieIds: + continue # Filtre des doublons + movieIds.add(sTmdbId) + + # Filtrage par titre + sTitle = movie[pbContent.TITLE].strip() + + # Titre recherché + if sSearchTitle: + if cUtil().CheckOccurence(sSearchTitle, sTitle) == 0: + continue + + # Recherche alphabétique + if sAlpha: + if sTitle[0].upper() != sAlpha: + continue + + sDisplayTitle = sTitle + + # Filtrage par années + movieYear = '' + if pbContent.YEAR >= 0: + movieYear = movie[pbContent.YEAR].strip() + # sDisplayTitle = '%s (%s)' % (sTitle, movieYear) + if sYear: + if sYear == UNCLASSIFIED: + if movieYear != '': + continue + elif not movieYear or sYear != movieYear: + continue + + # Filtrage par résolutions vidéos + listRes = None + + if 'film' in sMedia: + if pbContent.RES>=0: + res = movie[pbContent.RES].strip() + listRes = [] + if '[' in res: + listRes.extend(eval(res)) + else: + listRes.append(res) + if len(listRes) == 0: + listRes.append('') + + if sRes: + if sRes == UNCLASSIFIED: + if '' not in listRes: + continue + elif sRes not in listRes: + continue + + nbItem += 1 + progress_.VSupdate(progress_, ITEM_PAR_PAGE) + if progress_.iscanceled(): + break + + sUrl = URL_MAIN + if sMedia : sUrl += '&sMedia=' + sMedia + if pasteID: sUrl += '&pasteID=' + pasteID + if movieYear : sUrl += '&sYear=' + movieYear + if sTmdbId: sUrl += '&idTMDB=' + sTmdbId + sUrl += '&sTitle=' + sTitle + + # Pour supporter les caracteres '&' et '+' dans les noms alors qu'ils sont réservés + sTitle = sTitle.replace('+', ' ').replace(' & ', ' | ') + sTitle = sTitle.replace('[', '').replace(']', '') # Exemple pour le film [REC], les crochets sont génants pour certaines fonctions + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sMovieTitle', sTitle) + if sTmdbId: oOutputParameterHandler.addParameter('sTmdbId', sTmdbId) # Utilisé par TMDB + if movieYear : oOutputParameterHandler.addParameter('sYear', movieYear) # Utilisé par TMDB + if listRes: oOutputParameterHandler.addParameter('listRes', listRes) + + if sMedia == 'serie': + oGui.addTV(SITE_IDENTIFIER, 'showSerieSaisons', sDisplayTitle, 'series.png', '', '', oOutputParameterHandler) + elif sMedia == 'anime': + oGui.addAnime(SITE_IDENTIFIER, 'showSerieSaisons', sDisplayTitle, 'animes.png', '', '', oOutputParameterHandler) + else: + oGui.addMovie(SITE_IDENTIFIER, 'showHosters', sDisplayTitle, 'films.png', '', '', oOutputParameterHandler) + + # Gestion de la pagination + if not sSearch: + + if nbItem % ITEM_PAR_PAGE == 0 and numPage*ITEM_PAR_PAGE < len(movies) : + numPage += 1 + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', siteUrl) + oOutputParameterHandler.addParameter('numPage', numPage) + oOutputParameterHandler.addParameter('numItem', numItem) + oGui.addNext(SITE_IDENTIFIER, 'showMovies', '[COLOR teal]Page ' + str(numPage) + ' >>>[/COLOR]', oOutputParameterHandler) + break + + progress_.VSclose(progress_) + + if not sSearch: + oGui.setEndOfDirectory() + + +def showSerieSaisons(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + searchTitle = oInputParameterHandler.getValue('sMovieTitle') + searchYear = oInputParameterHandler.getValue('sYear') + + # Pour supporter les caracteres '&' et '+' dans les noms alors qu'ils sont réservés + sUrl = siteUrl.replace('+', ' ').replace('|', '+').replace(' & ', ' | ') + + sUrl, params = sUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + idTMDB = aParams['idTMDB'] if 'idTMDB' in aParams else None + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'serie' + + saisons = [] + listeIDs = getPasteList(sUrl, pasteID) + pbContent = PasteBinContent() + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + + # Recherche les saisons de la série + for serie in moviesBin: + + if pbContent.CAT >=0 and sMedia not in moviesBin[pbContent.CAT]: + continue + + # Recherche par id + found = False + if idTMDB and pbContent.TMDB >= 0: + sMovieID = serie[pbContent.TMDB].strip() + if sMovieID: + if sMovieID != idTMDB: + continue + found = True + + # Sinon, recherche par titre/année + if not found: + if pbContent.CAT >= 0 and 'serie' not in serie[pbContent.CAT]: + continue + if searchYear and pbContent.YEAR >= 0: + sYear = serie[pbContent.YEAR].strip() + if sYear and sYear != searchYear: + continue + + sTitle = serie[pbContent.TITLE].strip() + if sTitle != searchTitle: + continue + + numSaison = serie[pbContent.SAISON].strip() + if numSaison not in saisons: + saisons.append(numSaison) + + + # Une seule saison, directement les épisodes + if len(saisons) == 1: + siteUrl += '&sSaison=' + saisons[0] + showEpisodesLinks(siteUrl) + return + + # Proposer les différentes saisons + for sSaison in sorted(saisons): + sUrl = siteUrl + '&sSaison=' + sSaison + + if sSaison.isdigit(): + sSaison = 'S{:02d}'.format(int(sSaison)) + + sDisplayTitle = searchTitle + ' - ' + sSaison + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sMovieTitle', sDisplayTitle) # on ne passe pas le sTitre afin de pouvoir mettre la saison en marque-page + oGui.addEpisode(SITE_IDENTIFIER, 'showEpisodesLinks', sDisplayTitle, 'series.png', '', '', oOutputParameterHandler) + + oGui.setEndOfDirectory() + + +def showEpisodesLinks(siteUrl = ''): + oGui = cGui() + + if not siteUrl: + oInputParameterHandler = cInputParameterHandler() + siteUrl = oInputParameterHandler.getValue('siteUrl') + + # Pour supporter les caracteres '&' et '+' dans les noms alors qu'ils sont réservés + sUrl = siteUrl.replace('+', ' ').replace('|', '+').replace(' & ', ' | ') + + params = sUrl.split('&',1)[1] + aParams = dict(param.split('=') for param in params.split('&')) + sSaison = aParams['sSaison'] if 'sSaison' in aParams else None + searchTitle = aParams['sTitle'].replace(' | ', ' & ') + + if not sSaison: + oGui.setEndOfDirectory() + return + + lines = getHosterList(siteUrl)[0] + + listeEpisodes = [] + for episode in lines: + for numEpisode in episode.keys(): + if not numEpisode in listeEpisodes: + listeEpisodes.append(numEpisode) + + sDisplaySaison = sSaison + if sSaison.isdigit(): + sDisplaySaison = 'S{:02d}'.format(int(sSaison)) + + for episode in sorted(listeEpisodes): + sUrl = siteUrl + '&sEpisode=' + str(episode) + + if str(episode).isdigit(): + episode = '{}E{:02d}'.format(sDisplaySaison, int(episode)) + else: + episode = '{}{}'.format(sDisplaySaison, episode) + sDisplayTitle = searchTitle + ' - ' + episode + + oOutputParameterHandler = cOutputParameterHandler() + oOutputParameterHandler.addParameter('siteUrl', sUrl) + oOutputParameterHandler.addParameter('sMovieTitle', sDisplayTitle) + oGui.addEpisode(SITE_IDENTIFIER, 'showHosters', sDisplayTitle, 'series.png', '', '', oOutputParameterHandler) + + oGui.setEndOfDirectory() + + +def showHosters(): + oGui = cGui() + oInputParameterHandler = cInputParameterHandler() + sTitle = oInputParameterHandler.getValue('sMovieTitle') + siteUrl = oInputParameterHandler.getValue('siteUrl') + + listHoster, listRes = getHosterList(siteUrl) + + if len(listRes) != len (listHoster): + listRes = None + + resIdx = 0 + for sHosterUrl in listHoster: + + if not sHosterUrl.startswith('http'): + sHosterUrl += 'http://'+ sHosterUrl + + oHoster = cHosterGui().checkHoster(sHosterUrl) + if (oHoster != False): + + if listRes: + res = listRes[resIdx] + if res.isdigit(): res += 'p' + res = res.replace('P', 'p').replace('1080p', 'HD').replace('720p', 'HD').replace('2160p', '4K') + sDisplayName = sTitle + if res: sDisplayName += ' [%s]' %res + resIdx += 1 + else: + sDisplayName = sTitle + + oHoster.setDisplayName(sDisplayName) + oHoster.setFileName(sTitle) + cHosterGui().showHoster(oGui, oHoster, sHosterUrl, '') + + oGui.setEndOfDirectory() + +# Retrouve tous les liens disponibles pour un film, ou un épisode, gère les groupes multipaste +def getHosterList(siteUrl): + # Pour supporter les caracteres '&' et '+' dans les noms alors qu'ils sont réservés + sUrl = siteUrl.replace('+', ' ').replace('|', '+').replace(' & ', ' | ') + + siteUrl, params = sUrl.split('&',1) + aParams = dict(param.split('=') for param in params.split('&')) + sMedia = aParams['sMedia'] if 'sMedia' in aParams else 'film' + pasteID = aParams['pasteID'] if 'pasteID' in aParams else None + searchYear = aParams['sYear'] if 'sYear' in aParams else None + searchSaison = aParams['sSaison'] if 'sSaison' in aParams else None + searchEpisode = aParams['sEpisode'] if 'sEpisode' in aParams else None + idTMDB = aParams['idTMDB'] if 'idTMDB' in aParams else None + searchTitle = aParams['sTitle'].replace(' | ', ' & ') + + pbContent = PasteBinContent() + movies = [] + listeIDs = getPasteList(siteUrl, pasteID) + for pasteBin in listeIDs: + sUrl = URL_MAIN + pasteBin + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + moviesBin = pbContent.getLines(sContent) + movies += moviesBin + + listHoster = [] + listRes = [] + + for movie in movies: + + # Filtrer par catégorie + if pbContent.CAT >=0 and sMedia not in movie[pbContent.CAT]: + continue + + # Filtrage par saison + if searchSaison and pbContent.SAISON >= 0: + sSaisons = movie[pbContent.SAISON].strip() + if sSaisons and searchSaison != sSaisons: + continue + + # Recherche par id + found = False + if idTMDB and pbContent.TMDB >= 0: + sMovieID = movie[pbContent.TMDB].strip() + if sMovieID: + if sMovieID != idTMDB: + continue + found = True + + # sinon, recherche par titre/année + if not found: + # Filtrage par années + if searchYear and pbContent.YEAR >= 0: + sYear = movie[pbContent.YEAR].strip() + if sYear and sYear != searchYear: + continue + + # Filtrage par titre + sTitle = movie[pbContent.TITLE].strip() + if sTitle != searchTitle: + continue + + links = movie[pbContent.URLS] + if "[" in links: + links = eval(links) + listHoster.extend(links) + elif isinstance(links, list): + listHoster.extend(links) + else: + if searchEpisode: + for numEpisode, link in links.items(): + if str(numEpisode) == searchEpisode: + listHoster.append(link) + break + else: + listHoster.append(links) + + # Retrouve les résolutions pour les films + if pbContent.RES >= 0 and 'film' in sMedia: + res = movie[pbContent.RES].strip() + if '[' in res: + listRes.extend(eval(res)) + else: + listRes.append(res) + if len(listRes) < len(links): # On complete les résolutions manquantes + for _ in range(len(links) - len(listRes)): + listRes.append('') + + # Supprime les doublons en gardant l'ordre +# links = [links.extend(elem) for elem in listHoster if not elem in links] + + return listHoster, listRes + + +# Ajout d'un lien pastebin +def addPasteName(): + oGui = cGui() + addons = addon() + + # Recherche d'un setting de libre + names = set() + newID = 0 + for numID in range(1, PASTE_PAR_GROUPE): + pasteLabel = addons.getSetting(SETTING_PASTE_LABEL + str(numID)) + if pasteLabel == '': + if newID == 0: + newID = numID + else: + names.add(pasteLabel) # Labels déjà utilisés + + settingLabel = SETTING_PASTE_LABEL + str(newID) + + + # Demande du label et controle si déjà existant + sLabel = oGui.showKeyBoard('', "Saisir un nom") + if sLabel == False: + return + if sLabel in names: + dialog().VSok(addons.VSlang(30082)) + return + + # Enregistrer Label/id dans les settings + addons.setSetting(settingLabel, sLabel) + dialog().VSinfo(addons.VSlang(30042)) + + oGui.updateDirectory() + + +# Retourne la liste des PasteBin depuis l'URL ou un groupe +def getPasteList(siteUrl, pasteID): + addons = addon() + + IDs = [] + + siteId = siteUrl.split(URL_MAIN) # Supporte le format https://pastebin.com/raw/izu23hfkjhd + if siteId[1]: + IDs.append(siteId[1]) + + if pasteID: + prefixID = SETTING_PASTE_ID + str(pasteID) + pasteBin = addons.getSetting(prefixID) + if pasteBin and pasteBin not in IDs: + IDs.append(pasteBin) + for numID in range(1, PASTE_PAR_GROUPE): + pasteID = prefixID + '_' + str(numID) + pasteBin = addons.getSetting(pasteID) + if pasteBin and pasteBin not in IDs: + IDs.append(pasteBin) + return IDs + + +# Ajout d'un lien pastebin +def addPasteID(): + oGui = cGui() + addons = addon() + + oInputParameterHandler = cInputParameterHandler() + pasteID = oInputParameterHandler.getValue('pasteID') + + # Recherche d'un setting de libre + # Et lister les pastes déjà déclarés pour éviter les doublons + IDs = set() + settingID = None + prefixID = SETTING_PASTE_ID + str(pasteID) + pasteBin = addons.getSetting(prefixID) + if pasteBin: + IDs.add(pasteBin) # IDs déjà renseigné + if pasteBin == '': + settingID = prefixID + for numID in range(1, PASTE_PAR_GROUPE): + pasteID = prefixID + '_' + str(numID) + pasteBin = addons.getSetting(pasteID) + if pasteBin != '': + IDs.add(pasteBin) # IDs déjà renseigné + elif not settingID: + settingID = pasteID + + # Demande de l'id PasteBin + pasteID = oGui.showKeyBoard('', "Saisir l'ID du PasteBin") + if pasteID == False: + return + if pasteID in IDs: # ID déjà dans le groupe + dialog().VSok(addons.VSlang(30082)) + return + + # Vérifier l'entete du Paste + pbContentNew = PasteBinContent() + sUrl = URL_MAIN + pasteID + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + + try: + movies = pbContentNew.getLines(sContent) + if len(movies) == 0 : + dialog().VSok(addons.VSlang(30022)) + return + except Exception: + dialog().VSinfo(addons.VSlang(30011)) + raise + + # Vérifier que les autres pastes du groupe ont le même format d'entete + if len(IDs)>0: + pbContentOld = PasteBinContent() + sUrl = URL_MAIN + IDs.pop() + oRequestHandler = cRequestHandler(sUrl) + oRequestHandler.setTimeout(4) + sContent = oRequestHandler.request() + pbContentOld.getLines(sContent) + + if not pbContentNew.isFormat(pbContentOld): + dialog().VSok(addons.VSlang(30022)) + return + + addons.setSetting(settingID, pasteID) + dialog().VSinfo(addons.VSlang(30042)) + + oGui.updateDirectory() + + +# Retirer un groupe PasteBin +def deletePasteName(): + + addons = addon() + if not dialog().VSyesno(addons.VSlang(30456)): + return + + oInputParameterHandler = cInputParameterHandler() + pasteID = oInputParameterHandler.getValue('pasteID') + + labelSetting = SETTING_PASTE_LABEL + pasteID + addons.setSetting(labelSetting, '') + + prefixID = SETTING_PASTE_ID + str(pasteID) + addons.setSetting(prefixID, '') + + for numID in range(1, PASTE_PAR_GROUPE): + pasteID = prefixID + '_' + str(numID) + if addons.getSetting(pasteID): + addons.setSetting(pasteID, '') + + cGui().updateDirectory() + +# msgctxt "#30072" +# msgid "File deleted" +# msgstr "Fichier supprimé" diff --git a/.install/export_colors.sh b/.install/export_colors.sh new file mode 100755 index 0000000..0be35e0 --- /dev/null +++ b/.install/export_colors.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +[[ -f ~/.bash_aliases && ! -z $(cat ~/.bash_aliases | grep c_red) ]] && exit 0 + +echo 'export c_blinkfast="\033[6m" +export c_light="\033[1m" +export c_white_bg="\033[47m" +export c_blue="\033[34m" +export c_red_bg="\033[41m" +export c_hide="\033[8m" +export c_purple_bg="\033[45m" +export c_yellow_bg="\033[43m" +export c_dark="\033[2m" +export c_reverse="003[7m" +export c_underline="\033[4m" +export c_blinkslow="\033[5m" +export c_red="\033[31m" +export c_white="\033[37m" +export c_cyan_bg="\033[46m" +export c_italic="\033[3m" +export c_black="\033[30m" +export c_cross="\033[9m" +export c_green="\033[32m" +export c_purple="\033[35m" +export c_="\033[0m" +export c_cyan="\033[36m" +export c_black_bg="\033[40m" +export c_yellow="\033[33m" +export c_blue_bg="\033[44m" +export c_green_bg="\033[42m"' >> ~/.bash_aliases + +source ~/.bash_aliases + diff --git a/.install/ipfs-pi-stream/audio.jpg b/.install/ipfs-pi-stream/audio.jpg new file mode 100644 index 0000000..9850d25 Binary files /dev/null and b/.install/ipfs-pi-stream/audio.jpg differ diff --git a/.install/ipfs-pi-stream/enable-camera.sh b/.install/ipfs-pi-stream/enable-camera.sh new file mode 100755 index 0000000..010c0b4 --- /dev/null +++ b/.install/ipfs-pi-stream/enable-camera.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +# Enable camera on Raspberry Pi +# set_config_var taken from raspi-config +set_config_var() { + lua - "$1" "$2" "$3" < "$3.bak" + local key=assert(arg[1]) + local value=assert(arg[2]) + local fn=assert(arg[3]) + local file=assert(io.open(fn)) + local made_change=false +for line in file:lines() do + if line:match("^#?%s*"..key.."=.*$") then + line=key.."="..value + made_change=true + end + print(line) +end + +if not made_change then + print(key.."="..value) +end +EOF +sudo mv "$3.bak" "$3" +} + +# Command extracted from raspi-config +sed /boot/config.txt -i -e "s/^startx/#startx/" +sed /boot/config.txt -i -e "s/^fixup_file/#fixup_file/" +set_config_var start_x 1 /boot/config.txt +set_config_var gpu_mem 128 /boot/config.txt diff --git a/.install/ipfs-pi-stream/install b/.install/ipfs-pi-stream/install new file mode 100755 index 0000000..571e541 --- /dev/null +++ b/.install/ipfs-pi-stream/install @@ -0,0 +1,52 @@ +#!/usr/bin/env bash + +set -e + +BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +# Enable camera on the Raspberry Pi +sudo "$BASE_DIR/enable-camera.sh" + +# Install ffmpeg and supporting tools + +sudo apt-get install -y ffmpeg lsof inotify-tools nginx + +# Copy placeholder for audio-only streams +cp "$BASE_DIR/audio.jpg" "$HOME/audio.jpg" + +# Add user to be able to modify nginx directories +sudo usermod -a -G "$USER" www-data +sudo chmod g+rw /var/www/html + +# TODO: why is this needed? +sudo chmod a+rw /var/www/html + +sudo cp -f "$BASE_DIR/process-stream.sh" /usr/bin/process-stream.sh +sudo cp -f "$BASE_DIR/process-stream.service" /etc/systemd/system/process-stream.service +sudo systemctl daemon-reload +sudo systemctl enable process-stream + +# Add hourly job to clear out old data +echo "41 * * * * $USER /usr/local/bin/ipfs repo gc" | sudo tee --append /etc/crontab + +# Install the ipfs video player +mkdir "$BASE_DIR/tmp" +current_dir="$(pwd)" + +git clone https://github.com/tomeshnet/ipfs-live-streaming.git "$BASE_DIR/tmp/ipfs-live-streaming" +cd "$BASE_DIR/tmp/ipfs-live-streaming" +git checkout b9be352582317e5336ddd7183ecf49042dafb33e +cd "$current_dir" + +VIDEO_PLAYER_PATH="$BASE_DIR/tmp/ipfs-live-streaming/terraform/shared/video-player" +sed -i s#__IPFS_GATEWAY_SELF__#/ipfs/# "$VIDEO_PLAYER_PATH/js/common.js" +sed -i s#__IPFS_GATEWAY_ORIGIN__#https://ipfs.io/ipfs/# "$VIDEO_PLAYER_PATH/js/common.js" +IPFS_ID=$(ipfs id | grep ID | head -n 1 | awk -F\" '{print $4}') +sed -i "s#live.m3u8#/ipns/$IPFS_ID#" "$VIDEO_PLAYER_PATH/js/common.js" +sed -i s#__M3U8_HTTP_URLS__#\ # "$VIDEO_PLAYER_PATH/js/common.js" +cp -r "$VIDEO_PLAYER_PATH" /var/www/html/video-player +rm -rf "$BASE_DIR/tmp" + +# Add entry into nginx home screen +APP="

IPFS Pi Stream Player

IPFS Video player for Pi Stream.
M3U8 Stream located over ipns
Go and play with built in video player
" +sudo sed -i "s#<\!--APPLIST-->#$APP\n<\!--APPLIST-->#" "/var/www/html/index.html" diff --git a/.install/ipfs-pi-stream/process-stream.service b/.install/ipfs-pi-stream/process-stream.service new file mode 100644 index 0000000..cd3166b --- /dev/null +++ b/.install/ipfs-pi-stream/process-stream.service @@ -0,0 +1,16 @@ +[Unit] +Description=Service to process RTMP stream +Wants=network.target +After=ipfs.service + +[Service] +Type=simple +User=pi +Group=pi +ExecStart=/usr/bin/process-stream.sh +ExecStop=/bin/kill -s QUIT $MAINPID +Restart=on-failure +RestartSec=10s + +[Install] +WantedBy=multi-user.target diff --git a/.install/ipfs-pi-stream/process-stream.sh b/.install/ipfs-pi-stream/process-stream.sh new file mode 100755 index 0000000..53b92a0 --- /dev/null +++ b/.install/ipfs-pi-stream/process-stream.sh @@ -0,0 +1,124 @@ +#!/usr/bin/env bash + +HLS_TIME=40 +M3U8_SIZE=3 +IPFS_GATEWAY="https://ipfs.io" + +# Load settings + +# Prepare Pi Camera +sudo modprobe bcm2835-v4l2 +sudo v4l2-ctl --set-ctrl video_bitrate=100000 + +function startFFmpeg() { + while true; do + mv ~/ffmpeg.log ~/ffmpeg.1 + echo 1 > ~/stream-reset + + # Stream Raspberry Pi Camera + ffmpeg -f video4linux2 -input_format h264 -video_size 1280x720 -framerate 30 -i /dev/video0 -vcodec copy -hls_time "${HLS_TIME}" "${what}.m3u8" > ~/ffmpeg.log 2>&1 + + # Stream FM Station from a SDR module (see contrib/pi-stream to install drivers) + # Frequency ends in M IE 99.9M + # rtl_fm -f 99.9M -M fm -s 170k -A std -l0 -E deemp -r 44.1k | ffmpeg -r 15 -loop 1 -i ../audio.jpg -f s16le -ac 1 -i pipe:0 -c:v libx264 -tune stillimage -preset ultrafast -hls_time "${HLS_TIME}" "${what}.m3u8" > ~/ffmpeg 2>&1 + + sleep 0.5 + done +} + +# Create directory for HLS content + +currentpath="$HOME/live" +sudo umount "${currentpath}" +rm -rf "${currentpath}" +mkdir "${currentpath}" +sudo mount -t tmpfs tmpfs "${currentpath}" +# shellcheck disable=SC2164 +cd "${currentpath}" + +what="$(date +%Y%m%d%H%M)-LIVE" + +# Start ffmpeg in background +startFFmpeg & + +while true; do +#TODO# Fix this one +# shellcheck disable=SC2086,SC2012 + nextfile=$(ls -tr ${what}*.ts 2>/dev/null | head -n 1) + + if [ -n "${nextfile}" ]; then + # Check if the next file on the list is still being written to by ffmpeg + if lsof "${nextfile}" | grep -1 ffmpeg; then + # Wait for file to finish writing + # If not finished in 45 seconds something is wrong, timeout + inotifywait -e close_write "${nextfile}" -t ${HLS_TIME} + fi + + # Grab the timecode from the m3u8 file so we can add it to the log + timecode=$(grep -B1 "${nextfile}" "${what}.m3u8" | head -n1 | awk -F : '{print $2}' | tr -d ,) + attempts=5 + until [[ "${timecode}" || ${attempts} -eq 0 ]]; do + # Wait and retry + sleep 0.5 + timecode=$(grep -B1 "${nextfile}" "${what}.m3u8" | head -n1 | awk -F : '{print $2}' | tr -d ,) + attempts=$((attempts-1)) + done + + if ! [[ "${timecode}" ]]; then + # Set approximate timecode + timecode="${HLS_TIME}.000000" + fi + + reset_stream=$(cat ~/stream-reset) + reset_stream_marker='' + if [[ ${reset_stream} -eq '1' ]]; then + reset_stream_marker=" #EXT-X-DISCONTINUITY" + fi + + echo 0 > ~/stream-reset + # Current UTC date for the log + time=$(date "+%F-%H-%M-%S") + + # Add ts file to IPFS + ret=$(ipfs add --pin=false "${nextfile}" 2>/dev/null > ~/tmp.txt; echo $?) + attempts=5 + until [[ ${ret} -eq 0 || ${attempts} -eq 0 ]]; do + # Wait and retry + sleep 0.5 + ret=$(ipfs add --pin=false "${nextfile}" 2>/dev/null > ~/tmp.txt; echo $?) + attempts=$((attempts-1)) + done + if [[ ${ret} -eq 0 ]]; then + # Update the log with the future name (hash already there) + echo "$(cat ~/tmp.txt) ${time}.ts ${timecode}${reset_stream_marker}" >> ~/process-stream.log + + # Remove nextfile and tmp.txt + rm -f "${nextfile}" ~/tmp.txt + + # Write the m3u8 file with the new IPFS hashes from the log + totalLines="$(wc -l ~/process-stream.log | awk '{print $1}')" + + sequence=0 + if ((totalLines>M3U8_SIZE)); then + sequence=$((totalLines-M3U8_SIZE)) + fi + { + echo "#EXTM3U" + echo "#EXT-X-VERSION:3" + echo "#EXT-X-TARGETDURATION:${HLS_TIME}" + echo "#EXT-X-MEDIA-SEQUENCE:${sequence}" + } > current.m3u8 + tail -n ${M3U8_SIZE} ~/process-stream.log | awk '{print $6"#EXTINF:"$5",\n'${IPFS_GATEWAY}'/ipfs/"$2}' | sed 's/#EXT-X-DISCONTINUITY#/#EXT-X-DISCONTINUITY\n#/g' >> current.m3u8 + + # Add m3u8 file to IPFS and IPNS publish + m3u8hash=$(ipfs add current.m3u8 | awk '{print $2}') + ipfs name publish --timeout=5s "${m3u8hash}" & + + # Copy files to web server + cp current.m3u8 /var/www/html/live.m3u8 + cp ~/process-stream.log /var/www/html/live.log + fi + else + sleep 5 + fi +done diff --git a/.install/ipfs-pi-stream/uninstall b/.install/ipfs-pi-stream/uninstall new file mode 100755 index 0000000..1c0a574 --- /dev/null +++ b/.install/ipfs-pi-stream/uninstall @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +set -e + +BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +sudo systemctl stop process-stream +sudo systemctl disable process-stream +sudo rm -f /usr/bin/process-stream.sh +sudo rm -f /etc/systemd/system/process-stream.service +sudo systemctl daemon-reload + +# Remove ffmpeg and supporting tools +sudo apt-get -y remove ffmpeg lsof inotify-tools + +# Revert permissions +sudo chmod 755 /var/www/html +sed -i "/ipfs repo gc/d" | sudo tee --append /etc/crontab diff --git a/.install/ipfs.sh b/.install/ipfs.sh new file mode 100755 index 0000000..eedc570 --- /dev/null +++ b/.install/ipfs.sh @@ -0,0 +1,69 @@ +#!/bin/bash +ipfs() { +# Install IPFS + +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized + +## Config +templates="$MY_PATH/.install/templates/ipfs" + +if [ "$EUID" -eq 0 ] + then echo -e "${c_red}DO NOT EXECUTE AS root. Choose a user for your Astroport Station (we like pi)$c_" + exit 1 +else echo -e "${c_yellow}OK $USER, let's go!$c_"; +fi + +[[ -d ~/.ipfs ]] && echo "IPFS install exist, please remove backup before execute this script" && exit 1 + +echo -e "${c_yellow}Onboarding IPFS...$c_" +[[ -f /usr/local/bin/ipfs ]] && sudo service ipfs stop + + +if [[ $ARM == "yes" ]]; then + wget https://dist.ipfs.io/ipfs-update/v1.5.2/ipfs-update_v1.5.2_linux-arm.tar.gz -O $MY_PATH/ipfs-update.tar.gz || err+="Download ipfs-update" +else + wget https://dist.ipfs.io/ipfs-update/v1.5.2/ipfs-update_v1.5.2_linux-amd64.tar.gz -O $MY_PATH/ipfs-update.tar.gz || err+="Download ipfs-update" +fi + +echo "INSTALL ipfs-update" +sudo tar -xvzf $MY_PATH/ipfs-update.tar.gz -C /usr/src/ || err+="Untar ipfs-update" +rm $MY_PATH/ipfs-update.tar.gz +cd /usr/src/ipfs-update/ +sudo ./install.sh || err+="Install ipfs-update" +cd $MY_PATH + +echo "INSTALL latest ipfs" +sudo ipfs-update install latest || err+="Install IPFS" + +echo "CREATE SYSTEMD ipfs SERVICE" +sudo cp -f $templates/ipfs.service /etc/systemd/system/ +sudo sed -i "s/_USER/$USER/g" /etc/systemd/system/ipfs.service + +sudo systemctl daemon-reload || err+="Restart IPFS" +sudo systemctl enable ipfs || err+="Enable IPFS daemon" + +# INIT ipfs +ipfs init -p lowpower +# ipfs init -p server ## Uncomment for server infrastructure + +# ACTIVATE CONFIG OPTIONS +# PUBSUB +ipfs config Pubsub.Router gossipsub +# MAXSTORAGE +availableDiskSize=$(df -P ~/ | awk 'NR>1{sum+=$4}END{print sum}') +diskSize="$((availableDiskSize / 2))" +ipfs config Datastore.StorageMax $diskSize +## PORT FORWARD (SSH) +ipfs config --json Experimental.Libp2pStreamMounting true + +######### UPDATE BOOTSTRAP LIST ########### +ipfs bootstrap rm --all + +sudo systemctl restart ipfs || err+="Restart IPFS daemon" + + +exit 0 +} + +$@ diff --git a/.install/ipfs_alone.sh b/.install/ipfs_alone.sh new file mode 100755 index 0000000..3b6eef9 --- /dev/null +++ b/.install/ipfs_alone.sh @@ -0,0 +1,135 @@ +#!/bin/bash +######################################################################## +{ # this ensures the entire script is downloaded # +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +# CHECK not root user !! +if [ "$EUID" -eq 0 ] + then echo -e "${c_red}DO NOT EXECUTE AS root. Choose a user for your Astroport Station (we like pi)$c_" + exit 1 +else echo -e "${c_yellow}OK $USER, let's go!$c_"; +fi + +# Ask user password on start +sudo true + +## Error funciton +err() { + echo -e "${c_red}$1$c_" + exit 1 +} + +# CHECK if daemon is already running +if [[ $(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) ]]; then + echo "ipfs daemon already running...! Must STOP ipfs AND remove ~/.ipfs to install again !!" + ipfs id && echo "ipfs swarm peers: " && ipfs swarm peers + echo "ipfs bootstrap list: " && ipfs bootstrap list + echo "Please RUN : sudo systemctl stop ipfs" + exit 1 +fi + +[[ -d ~/.ipfs ]] && echo "IPFS install exist! Please remove or backup before executing this script" && exit 1 + +echo -e "${c_yellow}Astroport IPFS Layer installation...$c_" + +# CHECK node IP isLAN? +myIP=$(hostname -I | awk '{print $1}') +isLAN=$(echo $myIP | grep -E "/(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + +MACHINE_TYPE=`uname -m` + +if [ ${MACHINE_TYPE} == 'x86_64' ]; then + curl -s https://dist.ipfs.io/ipfs-update/v1.6.0/ipfs-update_v1.6.0_linux-amd64.tar.gz -o $MY_PATH/ipfs-update.tar.gz +elif [ ${MACHINE_TYPE:0:3} == 'arm' ]; then + curl -s https://dist.ipfs.io/ipfs-update/v1.6.0/ipfs-update_v1.6.0_linux-arm.tar.gz -o $MY_PATH/ipfs-update.tar.gz +else + [ ! -f $MY_PATH/ipfs-update.tar.gz ] && err "Your $MACHINE_TYPE is not supported yet... Please add an issue." +fi + +echo "INSTALL ipfs-update >>>>>>>>>>>>>>>>>>>>>>>>>>" +sudo tar -xvzf $MY_PATH/ipfs-update.tar.gz -C /usr/src/ || err "Untar ipfs-update" +rm $MY_PATH/ipfs-update.tar.gz +cd /usr/src/ipfs-update/ +sudo ./install.sh || err "Install ipfs-update" +cd $MY_PATH + +echo "INSTALL ipfs 0.7.0 >>>>>>>>>>>>>>>>>>>>>>>>>>" +sudo ipfs-update install 0.7.0 || err "Install IPFS" + +## DEBIAN +echo "CREATE SYSTEMD ipfs SERVICE >>>>>>>>>>>>>>>>>>" +cat > /tmp/ipfs.service <1{sum+=$4}END{print sum}') +diskSize="$((availableDiskSize / 2))" +ipfs config Datastore.StorageMax $diskSize +## Activate Rapid "ipfs p2p" +ipfs config --json Experimental.Libp2pStreamMounting true +ipfs config --json Experimental.P2pHttpProxy true + +######### MAKE DEFAULT BOOTSTRAP TO oasis.astroport.com 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS ########### +ipfs bootstrap rm --all +ipfs bootstrap add /dnsaddr/oasis.astroport.com/tcp/4001/ipfs/12D3KooWBYme2BsNUrtx4mEdNX6Yioa9AV7opWzQp6nrPs6ZKabN +ipfs bootstrap add /ip4/51.15.166.54/tcp/4001/p2p/12D3KooWBYme2BsNUrtx4mEdNX6Yioa9AV7opWzQp6nrPs6ZKabN +ipfs bootstrap add /ip4/51.15.166.54/udp/4001/quic/p2p/12D3KooWBYme2BsNUrtx4mEdNX6Yioa9AV7opWzQp6nrPs6ZKabN +ipfs bootstrap add /ip6/fe80::208:a2ff:fe0c:20d8/tcp/4001/p2p/12D3KooWBYme2BsNUrtx4mEdNX6Yioa9AV7opWzQp6nrPs6ZKabN +########################################### +# TODO: ADD some other bootstrap NODES +########################################### + +sudo systemctl start ipfs || err "Start IPFS daemon" + +sleep 3 + +echo "Peers: " && ipfs swarm peers && sleep 0.3 +#[[ ! $(ipfs swarm peers) =~ "/ip4/" ]] && err "No peers found in swarm. Please open issue :https://git.p2p.legal/axiom-team/astroport/issues" + +} # this ensures the entire script is downloaded # +# IPFS CONFIG documentation: https://github.com/ipfs/go-ipfs/blob/master/docs/config.md#addressesswarm diff --git a/.install/kodi/plugin.video.invidious-0.2.5.zip b/.install/kodi/plugin.video.invidious-0.2.5.zip new file mode 100644 index 0000000..f1a2878 Binary files /dev/null and b/.install/kodi/plugin.video.invidious-0.2.5.zip differ diff --git a/.install/kodi/repository.vstream-0.0.3.zip b/.install/kodi/repository.vstream-0.0.3.zip new file mode 100644 index 0000000..f1c12a1 Binary files /dev/null and b/.install/kodi/repository.vstream-0.0.3.zip differ diff --git a/.install/loveland.sh b/.install/loveland.sh new file mode 100755 index 0000000..8ea3d87 --- /dev/null +++ b/.install/loveland.sh @@ -0,0 +1,264 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 0.3 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +{ +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" +WHOAMI=$(sbotc whoami | jq -r .id) || er+=" sbotc whoami problem" +[[ "$YOU" == "" || "$IPFSNODEID" == "" || "$WHOAMI" == "" ]] && echo "ERROR : $er " && exit 1 +######################################################################## +#### DO NOT RUN AS ROOT +[[ $USER == "root" ]] && echo "DO NOT RUN AS root!! Use regular USER with sudo AUTHORISATION" && exit 1 +#### APACHE NOT SUPPORTED +is_apache_running=$(ps auxf --sort=+utime | grep -w apache | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +[[ $is_apache_running ]] && echo "SORRY ONLY nginx is supported. EXIT" && exit 1 +#### ARM / X64 NOT USED THERE +MACHINE_TYPE=`uname -m` +[ ${MACHINE_TYPE:0:3} == 'arm' ] && isARM="YES" + +### UPDATE apt cache +sudo apt-get update + +### Adding YOU to www-data group +sudo adduser $YOU www-data + +################################## +## INSTALL RAINBOW ASCII ;) +[[ ! $(which figlet) ]] && sudo apt install figlet -y +[[ ! $(which lolcat) ]] && sudo apt install lolcat -y + +echo ' +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ______ __ ____ ___ + / ____/___ ____ __ __/ / ____ _/ __ \____ _____/ (_)___ + / / / __ \/ __ \/ / / / / / __ `/ /_/ / __ `/ __ / / __ \ +/ /___/ /_/ / /_/ / /_/ / /___/ /_/ / _, _/ /_/ / /_/ / / /_/ / +\____/\____/ .___/\__, /_____/\__,_/_/ |_|\__,_/\__,_/_/\____/ + /_/ /____/ + +Multimedia Layer (https://www.copylaradio.com) +' | lolcat +## MULTIMEDIA +## VIDEO & AUDIO & PLAYLISTS ~/.zen/ DIR +mkdir -p ~/.zen/video +mkdir -p ~/.zen/audio +mkdir -p ~/.zen/playlists + +######## YOUTUBE-DL ########## +if [[ ! $(which youtube-dl) ]]; then + sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl || err=1 + sudo chmod a+rx /usr/local/bin/youtube-dl + sudo chown $YOU /usr/local/bin/youtube-dl +fi + +############################### +# MPD/MPC RompR AUDIO LAYER +############################### +if [[ ! $(which mpd) ]]; then + sudo apt-get install libid3-tools mpd mpc lame ffmpeg lsof lltag inotify-tools bc -y || err=1 + sudo apt-get install lame sox libsox-fmt-mp3 eyed3 python-chardet imagemagick curl -y || err=1 #libav-tools unavailable on some system + sudo apt-get install ca-certificates git-core binutils rsync alsa-utils bc espeak mpg321 fuse atomicparsley -y || err=1 + + ## CONFIG MPD + sudo cp -f /home/$YOU/.zen/astroport/.install/templates/copylaradio/mpd.conf /etc/mpd.conf + sudo sed -i "s/_USER_/$YOU/g" /etc/mpd.conf || err=1 + mkdir ~/.config/mpd && sudo cp -f /etc/mpd.conf ~/.config/mpd/mpd.conf && sudo chown $YOU ~/.config/mpd/mpd.conf + + ## CHOWN mpd FILES STRUCTURE + sudo chown -R $YOU /var/lib/mpd/ /var/run/mpd /run/mpd /var/log/mpd + sudo service mpd restart || err=1 +fi + +### INSTALL NGINX +echo '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + _ + ____ ____ _(_)___ _ __ + / __ \/ __ `/ / __ \| |/_/ + / / / / /_/ / / / / /> < +/_/ /_/\__, /_/_/ /_/_/|_| + /____/ + +install +' | lolcat + +sudo apt-get install fail2ban nginx ssl-cert php-curl php-sqlite3 php-gd php-json php-xml php-mbstring php-fpm sqlite -y || err=1 + +[[ ! $(which nslookup) ]] && sudo apt-get install lolcat dnsutils -y + +echo '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + __ ___ _ ___ + / |/ /_ __ ____ ____ _____ ___ ___ (_)___/__ \ + / /|_/ / / / / / __ \/ __ `/ __ `__ \/ _ \ / / ___// _/ + / / / / /_/ / / / / / /_/ / / / / / / __/ / (__ )/_/ +/_/ /_/\__, / /_/ /_/\__,_/_/ /_/ /_/\___/ /_/____/(_) + /____/ +' | lolcat + +myIP=$(hostname -I | awk '{print $1}' | head -n 1) +isLAN=$(echo $myIP | grep -E "/(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + +# Ask to the router its name (BOX DNS or system defined) +[[ -f /home/$YOU/.zen/astroport/zen/tools/nodename ]] && NODENAME=$(/home/$YOU/.zen/astroport/zen/tools/nodename) \ +|| NODENAME=$(curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/nodename | bash) ## RUNNING ALONE !! + +echo $NODENAME + +###################################### +### LAUNCHIN OASIS = SSB HTTP interface +###################################### +echo ' + _ + ____ ____ ______(_)____ + / __ \/ __ `/ ___/ / ___/ +/ /_/ / /_/ (__ ) (__ ) +\____/\__,_/____/_/____/ + SSB DEMO HTTP interface +' | lolcat +# IN CASE, KILL RUNNING OASIS +isOASIS=$(ps auxf --sort=+utime | grep -w oasis | grep -v -E 'color=auto|grep' | tail -n 1 | awk '{print $2}') +[[ $isOASIS ]] && sudo kill -9 $isOASIS + +echo "Starting OASIS with good $NODENAME & network config" +echo "TODO: add to your /etc/rc.local or systemd or initV !!!" + +#if [[ ! $isLAN ]]; then +# ### TODO: unlock oasis restrictions!! CANNOT MAKE PRIVATE MESSAGE => Feddless.social CAN add it as module on loveland portal!!) +# oasis --allow-host $NODENAME --host $NODENAME --public 2>&1>/dev/null & +# echo "--public = OASIS STATION IS IN VIEWING MODE ONLY..." +#else +# oasis --allow-host $NODENAME --host $NODENAME 2>&1>/dev/null & +#fi + +#sleep 5 + +echo ' + __ ____ _ __________ __ + / / / __ \ | / / ____/ / ____ _____ ____/ / + / / / / / / | / / __/ / / / __ `/ __ \/ __ / + / /___/ /_/ /| |/ / /___/ /___/ /_/ / / / / /_/ / +/_____/\____/ |___/_____/_____/\__,_/_/ /_/\__,_/ + +Portal +' | lolcat + +### +echo "INSTALL LOVELand WebSite. Linking to /var/www ..." +if [[ ! -L /var/www/loveland ]]; then + sudo ln -s /home/$YOU/.zen/astroport/www/LOVELand /var/www/loveland +fi + +echo "JUKEBOX init" +[[ -d /var/www/loveland/jukebox/albumart ]] && sudo chmod -R 777 /var/www/loveland/jukebox/albumart +[[ -d /var/www/loveland/jukebox/prefs ]] && sudo chmod -R 777 /var/www/loveland/jukebox/prefs +[[ -d /var/www/loveland/g1barre/img/qrcodes ]] && sudo chmod -R 777 /var/www/loveland/g1barre/img/qrcodes + +# CONFIG NGINX - LOVE LAND FRONTAL WEB PAGE +echo "$NODENAME" | figlet -f slant | lolcat + +## Write NODENAME to IPFS +echo "$NODENAME" > /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename + +PHPVERSION=$(ps auxf | grep php-fpm | grep -v -E 'color=auto|grep' | head -n 1 | grep -oP '(?<=\().*(?=\))' | awk -F '/' '{print $4}') +### ASTROPORT STATION LOVELAND PORTAL +sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/loveland.conf +sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/loveland.conf +sudo sed -i "s/_PORT_/10010/g" /tmp/loveland.conf +sudo sed -i "s/_APPLI_//g" /tmp/loveland.conf +sudo cp -f /tmp/loveland.conf /etc/nginx/conf.d/loveland.conf + +### GCHANGE G1 Zen +sudo rm -f /etc/nginx/conf.d/gchange.conf +sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/gchange.conf +sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/gchange.conf +sudo sed -i "s/_PORT_/10020/g" /tmp/gchange.conf +sudo sed -i "s/_APPLI_/gchange/g" /tmp/gchange.conf +sudo cp -f /tmp/gchange.conf /etc/nginx/conf.d/gchange.conf + +### CESIUM G1 Zen +sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/cesium.conf +sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/cesium.conf +sudo sed -i "s/_PORT_/10030/g" /tmp/cesium.conf +sudo sed -i "s/_APPLI_/cesium/g" /tmp/cesium.conf +sudo cp -f /tmp/cesium.conf /etc/nginx/conf.d/cesium.conf + +### JUKEBOX RompR CopyLaRadio +if [[ $(which mpd) ]]; then + sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/jukebox.conf + sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/jukebox.conf + sudo sed -i "s/_PORT_/10011/g" /tmp/jukebox.conf + sudo sed -i "s/_APPLI_/jukebox/g" /tmp/jukebox.conf + sudo cp -f /tmp/jukebox.conf /etc/nginx/conf.d/jukebox.conf +fi + +### SSB OASIS Zen (PROXY MODE 10040 -> 3000) +if [[ $(which oasis) ]]; then + sudo sed "s/_MY_NODE_NAME_/$NODENAME/g" /home/$YOU/.zen/astroport/www/oasis.conf > /tmp/oasis.conf + sudo sed -i "s/_PORT_/10040/g" /tmp/oasis.conf + sudo sed -i "s/_LHOST_/$NODENAME:3000/g" /tmp/oasis.conf + sudo sed -i "s/_APPLI_//g" /tmp/oasis.conf + sudo cp -f /tmp/oasis.conf /etc/nginx/conf.d/oasis.conf +# TRICK: COULD BE USED TO ADD .htpasswod ACCESS CONTROL AND REMOVE --public +# TODO use "ipfs p2p" to AGREGATE ALL OASIS on ONE (not ALL like G1SMS) ? +# NEED G1PUB to be identified with same MEMBER owner in 'zen/ipfs_OPEN_ports.sh'? +fi + +### G1SMS propagation to localhost:10099 / 10097 ("ipfs p2p" forwarded) +if [[ $(which gammu) ]]; then +# DIRECT MODE + sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/g1sms.conf + sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/g1sms.conf + sudo sed -i "s/_PORT_/10099/g" /tmp/g1sms.conf + sudo sed -i "s/_APPLI_/g1sms/g" /tmp/g1sms.conf + sudo cp -f /tmp/g1sms.conf /etc/nginx/conf.d/g1sms.conf +else +# PROXY MODE (10099 -> 10097) ### ipfs p2p PROPAGATION WITH 'zen/ipfs_OPEN_ports.sh' + sudo sed "s/_MY_NODE_NAME_/$NODENAME/g" /home/$YOU/.zen/astroport/www/oasis.conf > /tmp/g1sms_proxy.conf + sudo sed -i "s/_PORT_/10099/g" /tmp/g1sms_proxy.conf + sudo sed -i "s/_LHOST_/127\.0\.0\.1\:10097/g" /tmp/g1sms_proxy.conf + sudo sed -i "s/_APPLI_/g1sms/g" /tmp/g1sms_proxy.conf + sudo cp -f /tmp/g1sms_proxy.conf /etc/nginx/conf.d/g1sms_proxy.conf +fi + +##### RESTART NGINX +sudo systemctl restart nginx || err=1 + +if [[ $err ]]; then + + echo -e "${c_red}Installation de LOVELand bizarre??$c_" + echo "PLEASE... POST YOUR ISSUE! https://git.p2p.legal/axiom-team/astroport/issues" + + exit 1 +else + + echo -e "${c_green}LOVE Land a été installé avec succès$c_" + echo -e "LoveLand Portal link http://$NODENAME:10010 (TRY ME) + +Add ScuttleButt Astroport PUB Invitation: +${c_green}With Patchwork: "Join a server"$c_ +Or with Oasis: http://$NODENAME:3000/settings (dev mode, still buggy) +${c_light}oasis.astroport.com:8008::@UeiA9iqZ0/XTjmYBht230KGr44bsr+Tl5BXSUDFv8vo=.ed25519~jd9Z4y/d/xZCF7bfuSgQSiSGLMeWFhwMosKUFhFxeEY=" $c_ + +fi + +# Open LOVEland in browser +URL="http://$NODENAME" +path=$(which xdg-open || which gnome-open) + +xo () +{ + for var in "$@"; do + $path "$var"; + sleep 0.5 + done +} + +[[ -n $path ]] && xo $URL:10010 $URL:10020 $URL:10030 > /dev/null + +} # for script being completely downloaded before run diff --git a/.install/mpd_rompr.sh b/.install/mpd_rompr.sh new file mode 100755 index 0000000..4ee6f6a --- /dev/null +++ b/.install/mpd_rompr.sh @@ -0,0 +1,188 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 0.3 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +{ +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" +WHOAMI=$(sbotc whoami | jq -r .id) || er+=" sbotc whoami problem" +[[ "$YOU" == "" || "$IPFSNODEID" == "" || "$WHOAMI" == "" ]] && echo "ERROR : $er " && exit 1 +######################################################################## +#### DO NOT RUN AS ROOT +[[ $USER == "root" ]] && echo "DO NOT RUN AS root!! Use regular USER with sudo AUTHORISATION" && exit 1 +#### APACHE NOT SUPPORTED +is_apache_running=$(ps auxf --sort=+utime | grep -w apache | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +[[ $is_apache_running ]] && echo "SORRY ONLY nginx is supported. EXIT" && exit 1 +#### ARM / X64 NOT USED THERE +MACHINE_TYPE=`uname -m` +[ ${MACHINE_TYPE:0:3} == 'arm' ] && isARM="YES" + +### UPDATE apt cache +sudo apt-get update + +### Adding YOU to www-data group +sudo adduser $YOU www-data + +################################## +## INSTALL RAINBOW ASCII ;) +[[ ! $(which figlet) ]] && sudo apt install figlet -y +[[ ! $(which lolcat) ]] && sudo apt install lolcat -y + +echo ' +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ______ __ ____ ___ + / ____/___ ____ __ __/ / ____ _/ __ \____ _____/ (_)___ + / / / __ \/ __ \/ / / / / / __ `/ /_/ / __ `/ __ / / __ \ +/ /___/ /_/ / /_/ / /_/ / /___/ /_/ / _, _/ /_/ / /_/ / / /_/ / +\____/\____/ .___/\__, /_____/\__,_/_/ |_|\__,_/\__,_/_/\____/ + /_/ /____/ + +Multimedia Layer (https://www.copylaradio.com) +' | lolcat +## MULTIMEDIA +## VIDEO & AUDIO & PLAYLISTS ~/.zen/ DIR +mkdir -p ~/astroport/films +mkdir -p ~/astroport/animes +mkdir -p ~/astroport/series +mkdir -p ~/astroport/docus +mkdir -p ~/astroport/musiques + +mkdir -p ~/.zen/video +mkdir -p ~/.zen/audio +mkdir -p ~/.zen/playlists + +######## YOUTUBE-DL ########## +if [[ ! $(which youtube-dl) ]]; then + sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl || err=1 + sudo chmod a+rx /usr/local/bin/youtube-dl + sudo chown $YOU /usr/local/bin/youtube-dl +fi + +############################### +# MPD/MPC RompR AUDIO LAYER +############################### +if [[ ! $(which mpd) ]]; then + sudo apt-get install libid3-tools mpd mpc lame ffmpeg lsof lltag inotify-tools bc -y || err=1 + sudo apt-get install lame sox libsox-fmt-mp3 eyed3 python-chardet imagemagick curl -y || err=1 #libav-tools unavailable on some system + sudo apt-get install ca-certificates git-core binutils rsync alsa-utils bc espeak mpg321 fuse atomicparsley -y || err=1 + + ## CONFIG MPD + sudo cp -f /home/$YOU/.zen/astroport/.install/templates/copylaradio/mpd.conf /etc/mpd.conf + sudo sed -i "s/_USER_/$YOU/g" /etc/mpd.conf || err=1 + mkdir ~/.config/mpd && sudo cp -f /etc/mpd.conf ~/.config/mpd/mpd.conf && sudo chown $YOU ~/.config/mpd/mpd.conf + + ## CHOWN mpd FILES STRUCTURE + sudo chown -R $YOU /var/lib/mpd/ /var/run/mpd /run/mpd /var/log/mpd + sudo service mpd restart || err=1 +fi + +### INSTALL NGINX +echo '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + _ + ____ ____ _(_)___ _ __ + / __ \/ __ `/ / __ \| |/_/ + / / / / /_/ / / / / /> < +/_/ /_/\__, /_/_/ /_/_/|_| + /____/ + +install +' | lolcat + +sudo apt-get install fail2ban nginx ssl-cert php-curl php-sqlite3 php-gd php-json php-xml php-mbstring php-fpm sqlite -y || err=1 + +[[ ! $(which nslookup) ]] && sudo apt-get install lolcat dnsutils -y + +echo '++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + __ ___ _ ___ + / |/ /_ __ ____ ____ _____ ___ ___ (_)___/__ \ + / /|_/ / / / / / __ \/ __ `/ __ `__ \/ _ \ / / ___// _/ + / / / / /_/ / / / / / /_/ / / / / / / __/ / (__ )/_/ +/_/ /_/\__, / /_/ /_/\__,_/_/ /_/ /_/\___/ /_/____/(_) + /____/ +' | lolcat + +myIP=$(hostname -I | awk '{print $1}' | head -n 1) +isLAN=$(echo $myIP | grep -E "/(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + +# Ask to the router its name (BOX DNS or system defined) +[[ -f /home/$YOU/.zen/astroport/zen/tools/nodename ]] && NODENAME=$(/home/$YOU/.zen/astroport/zen/tools/nodename) \ +|| NODENAME=$(curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/nodename | bash) ## RUNNING ALONE !! + +echo $NODENAME + + +echo ' + __ ____ _ __________ __ + / / / __ \ | / / ____/ / ____ _____ ____/ / + / / / / / / | / / __/ / / / __ `/ __ \/ __ / + / /___/ /_/ /| |/ / /___/ /___/ /_/ / / / / /_/ / +/_____/\____/ |___/_____/_____/\__,_/_/ /_/\__,_/ + +Portal +' | lolcat + +### +echo "INSTALL LOVELand WebSite. Linking to /var/www ..." +if [[ ! -L /var/www/loveland ]]; then + sudo ln -s /home/$YOU/.zen/astroport/www/LOVELand /var/www/loveland +fi + +echo "JUKEBOX init" +[[ -d /var/www/loveland/jukebox/albumart ]] && sudo chmod -R 777 /var/www/loveland/jukebox/albumart +[[ -d /var/www/loveland/jukebox/prefs ]] && sudo chmod -R 777 /var/www/loveland/jukebox/prefs +[[ -d /var/www/loveland/g1barre/img/qrcodes ]] && sudo chmod -R 777 /var/www/loveland/g1barre/img/qrcodes + +# CONFIG NGINX - LOVE LAND FRONTAL WEB PAGE +echo "$NODENAME" | figlet -f slant | lolcat + +## Write NODENAME to IPFS +echo "$NODENAME" > /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename + +PHPVERSION=$(ps auxf | grep php-fpm | grep -v -E 'color=auto|grep' | head -n 1 | grep -oP '(?<=\().*(?=\))' | awk -F '/' '{print $4}') + +### JUKEBOX RompR CopyLaRadio +if [[ $(which mpd) ]]; then + sudo sed "s/_PHPVERSION_/$PHPVERSION/g" /home/$YOU/.zen/astroport/www/loveland.conf > /tmp/jukebox.conf + sudo sed -i "s/_MY_NODE_NAME_/$NODENAME/g" /tmp/jukebox.conf + sudo sed -i "s/_PORT_/80/g" /tmp/jukebox.conf + sudo sed -i "s/_APPLI_/jukebox/g" /tmp/jukebox.conf + sudo cp -f /tmp/jukebox.conf /etc/nginx/conf.d/jukebox.conf +fi + +##### RESTART NGINX +sudo systemctl restart nginx || err=1 + +if [[ $err ]]; then + + echo -e "${c_red}Installation de bizarre??$c_" + echo "PLEASE... POST YOUR ISSUE! https://git.p2p.legal/axiom-team/astroport/issues" + + exit 1 +else + + echo -e "${c_green}Installation réalisée avec succès$c_" + echo -e "Astroport JUKEBOX http://$NODENAME (TRY ME)" + +fi + +# Open in browser +URL="http://$NODENAME" +path=$(which xdg-open || which gnome-open) + +xo () +{ + for var in "$@"; do + $path "$var"; + sleep 0.5 + done +} + +[[ -n $path ]] && xo $URL:80 > /dev/null + +} # for script being completely downloaded before run diff --git a/.install/nextcloud/README.md b/.install/nextcloud/README.md new file mode 100644 index 0000000..0a53e21 --- /dev/null +++ b/.install/nextcloud/README.md @@ -0,0 +1,35 @@ +# Automatic install of Nextcloud on Debian 8/9/10 +## Use + +Set good variables for your use case at the beginning of install.sh: + +``` +nc_domain="" # Votre nom de domaine pour votre nextcloud. Si vide il prendra le premier argument que vous passerez, sinon le hostname de votre machine +nc_port=80 # Numéro de port d'écoute de nginx +admin_user="admin" # Le pseudo du compte admin +admin_pass="admin" # Le mot de passe que vous désirez pour le compte admin +db_pass="" # Le mot de passe que vous désirez pour MariaDB. Si vide, un mot de passe aléatoire sécurisé sera choisi +isSSL=false # true si nextcloud et nginx doivent être configuré en https +configMaria=auto # Mettez manual ou auto, attention auto est expérimental et vraiment pas recommendé +p2env=false # true si vous êtes dans un environnement p2p.legal +``` + +Then: + +``` +chmod u+x install.sh +./install.sh +``` + +You can change the ssl state of your instance after the installation if you need. +Just execute ssl.sh: + +`./ssl.sh` + +If you prefere, you can download this script directly via: + +``` +wget https://dev-nextcloud.p2p.legal/installeur/install-nextcloud.tar.gz +tar -zxvf install-nextcloud.tar.gz +./install.sh 2>&1 | tee loginstall.log +``` diff --git a/.install/nextcloud/install.sh b/.install/nextcloud/install.sh new file mode 100755 index 0000000..ee5ade7 --- /dev/null +++ b/.install/nextcloud/install.sh @@ -0,0 +1,186 @@ +#!/bin/bash +################################################################################ +# Author: poka (poka@p2p.legal) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ + + +### Variables ### + +nc_domain="" # Votre nom de domaine pour votre nextcloud. Si vide il prendra le premier argument que vous passerez, sinon le hostname de votre machine +nc_port=10050 # Numéro de port d'écoute de nginx +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) +admin_user="$YOU" # Le pseudo du compte admin +admin_pass="0penS0urce!" # Le mot de passe que vous désirez pour le compte admin +db_pass="" # Le mot de passe que vous désirez pour MariaDB. Si vide, un mot de passe aléatoire sécurisé sera choisi +data_dir="/home/$YOU/.zen/nextcloud" # Le répertoir data de nextcloud, toutes les données utilisateurs s'y trouvent +isSSL=false # true si nextcloud et nginx doivent être configuré en https +configMaria=auto # Mettez manual ou auto, attention auto est expérimental et vraiment pas recommendé +p2env=false # true si vous êtes dans un environnement p2p.legal + +################# + +if [ "$EUID" -ne 0 ] + then echo -e "${c_red}Veuillez executez ce script en root$c_" + exit 1 +fi + +## Atroport config +echo -e "${c_yellow}Getting local hostname...$c_" +nc_domain=$(/home/$YOU/.zen/astroport/zen/tools/nodename) +templates="/home/$YOU/.zen/astroport/.install/nextcloud/templates" + +## Set var +[[ -z $nc_domain ]] && nc_domain=$1 +[[ -z $nc_domain ]] && nc_domain=$(echo $HOSTNAME.p2p.legal) +[[ -z $db_pass ]] && db_pass="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)" && echo "Votre mot de passe mysql nextcloud est : $db_pass" >> /root/nextcloud_mysql_pwd.txt +db_pass_root="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9~!@#$%^&*_-' | fold -w 32 | head -n 1)" + +if [[ p2env == "true" ]]; then + cd /nextcloud/templates/ +else + [[ ! -e $templates ]] && echo -e "${c_red}Erreur: Le dossier templates n'existe pas, installation impossible.$c_" && exit 1 + cd $templates +fi + +## Update system packages + +apt install -y lsb-release apt-transport-https ca-certificates +apt update -y + +apt install nginx mariadb-server apt-transport-https curl gnupg2 git lsb-release ssl-cert ca-certificates apt-transport-https tree locate software-properties-common dirmngr screen htop net-tools zip unzip curl ffmpeg ghostscript libfile-fcntllock-perl -y + +systemctl start nginx || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +systemctl start mariadb || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +systemctl enable mariadb || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +systemctl enable nginx || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) + +echo -e "${c_yellow} === Installing php ... ===$c_" +apt install php php-fpm php-xml php-curl php-gd php php-cgi php-cli php-zip php-mysql php-mbstring php-intl php-json php-bz2 php-ldap php-apcu imagemagick php-imagick php-smbclient -y +export PHPVERSION=$(ps auxf | grep php-fpm | grep -v -E 'color=auto|grep' | head -n 1 | grep -oP '(?<=\().*(?=\))' | awk -F '/' '{print $4}') + +echo -e "${c_yellow} === Configuring php ... ===$c_" +[[ ! -e /etc/php/$PHPVERSION/cli/php.ini.bak ]] && (bash configure_php.sh || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1)) || echo "PHP déjà configuré, skip" + +echo -e "${c_yellow} === Configure MariaDB ===$c_" +configMariaManual() { + mysql_secure_installation || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +} +configMariaAuto() { + mysql -e "UPDATE mysql.user SET Password = PASSWORD('$db_pass_root') WHERE User = 'root'" + isLocalhostUsers=$(mysql -e "select user from mysql.user;" | grep "localhost") + isTestDB=$(mysql -e "show databases" | grep "test") + [[ -n $isLocalhostUsers ]] && mysql -e "DROP USER ''@'localhost'; DROP USER ''@'$(hostname)'" + [[ -n $isTestDB ]] && mysql -e "DROP DATABASE test" + mysql -e "FLUSH PRIVILEGES" +} + +[[ $configMaria == "auto" ]] && configMariaAuto || configMariaManual +[[ ! -e /etc/mysql/my.cnf.bak ]] && (mv /etc/mysql/my.cnf /etc/mysql/my.cnf.bak && cp my.cnf /etc/mysql/ && service mysql restart) || echo "MariaDB déjà configuré, skip" + +echo -e "${c_yellow} === Create and configure database... ===$c_" +isDBCreate=$(mysql -e "show databases" | grep "nextcloud") +[[ -z $isDBCreate ]] && mysql -e "CREATE DATABASE nextcloud;CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY \"$db_pass\";GRANT ALL PRIVILEGES ON nextcloud.* TO 'nextcloud'@'localhost';FLUSH PRIVILEGES;ALTER DATABASE nextcloud CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;" || echo "La base de donnée de nextcloud est déjà créé, skip" + +echo -e "${c_yellow} === Installing and configure Redis... ===$c_" +apt install redis-server php-redis -y + +[[ ! -e /etc/redis/redis.conf.bak ]] && cp /etc/redis/redis.conf /etc/redis/redis.conf.bak || echo "Redis est déjà configuré, skip" +sed -i "s/port 6379/port 0/" /etc/redis/redis.conf +sed -i "s/redis.sock/redis-server.sock/" /etc/redis/redis.conf +sed -i s/\#\ unixsocket/\unixsocket/g /etc/redis/redis.conf +sed -i "s/unixsocketperm 700/unixsocketperm 770/" /etc/redis/redis.conf +sed -i "s/# maxclients 10000/maxclients 512/" /etc/redis/redis.conf +usermod -a -G redis www-data || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +[[ ! -e /etc/sysctl.conf.bak ]] && cp /etc/sysctl.conf /etc/sysctl.conf.bak || echo "sysctl est déjà configuré, skip" +sed -i '$avm.overcommit_memory = 1' /etc/sysctl.conf + +service redis-server restart || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) + +echo -e "${c_yellow} === Installing NextCloud... ===$c_" + +if [[ ! -e /var/www/nextcloud ]]; then + mkdir /var/www/nextcloud + chown www-data:www-data /var/www/nextcloud + chmod 750 /var/www/nextcloud || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) + + wget https://download.nextcloud.com/server/releases/latest.tar.bz2 + tar -xjf latest.tar.bz2 -C /var/www && chown -R www-data:www-data /var/www/ && rm -f latest.tar.bz2 +fi + +if [[ ! -e $data_dir ]]; then + mkdir -p $data_dir + chown www-data:www-data $data_dir + chmod 750 $data_dir || (echo -e "${c_red}Erreur quelque part ...$c_" && exit 1) +fi + +## Add local IP as secondary trust domain +# Prefere occ methode ... +# sed -i "/0 => '$nc_domain'.*/a \ 1 => '$ip_local:$nc_port'," /var/www/nextcloud/config/config.php +ip_local=$(/sbin/ifconfig | sed -En 's/127.0.0.1//;s/.*inet (addr:)?(([0-9]*\.){3}[0-9]*).*/\2/p') +[[ $nc_port == 80 ]] && nc_port_loc="" || nc_port_loc=":$nc_port" + +[[ -e /var/www/nextcloud/config/config.php ]] && isNCConfig=$(cat /var/www/nextcloud/config/config.php | grep "'installed' => true") +[[ -z "$isNCConfig" ]] && sudo -u www-data php /var/www/nextcloud/occ maintenance:install --database "mysql" --database-name "nextcloud" --database-user "nextcloud" --database-pass "$db_pass" --admin-user "$admin_user" --admin-pass "$admin_pass" --data-dir "$data_dir" +sleep 0.2 +sudo -u www-data php /var/www/nextcloud/occ config:system:set mysql.utf8mb4 --type boolean --value="true" +sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 0 --value=$nc_domain +sudo -u www-data php /var/www/nextcloud/occ config:system:set trusted_domains 1 --value=$ip_local$nc_port_loc +sudo -u www-data php /var/www/nextcloud/occ config:system:set overwrite.cli.url --value=$nc_domain + +isNCConfigAdd=$(cat /var/www/nextcloud/config/config.php | grep "activity_expire_days") +if [[ -z "$isNCConfigAdd" ]]; then + sudo -u www-data sed -i 's/^[ ]*//' /var/www/nextcloud/config/config.php + sudo -u www-data sed -i '/);/d' /var/www/nextcloud/config/config.php + cat config_complete.php >> /var/www/nextcloud/config/config.php +fi + +sudo -u www-data sed -i "s/output_buffering=.*/output_buffering=0/" /var/www/nextcloud/.user.ini + +sudo -u www-data php /var/www/nextcloud/occ app:disable survey_client +sudo -u www-data php /var/www/nextcloud/occ app:disable firstrunwizard +sudo -u www-data php /var/www/nextcloud/occ app:enable admin_audit +sudo -u www-data php /var/www/nextcloud/occ app:enable files_pdfviewer + +echo -e "${c_yellow} === Configure nginx ... ===$c_" +cp nextcloud.conf /etc/nginx/conf.d/ +cp *optimization.conf /etc/nginx/ +sed -i "s/NC_DOMAIN/$nc_domain/" /etc/nginx/conf.d/nextcloud.conf +sed -i "s/NC_PORT/$nc_port/" /etc/nginx/conf.d/nextcloud.conf +sed -i "s/_PHPVERSION/$PHPVERSION/" /etc/nginx/conf.d/nextcloud.conf +sed -i "s/80 default_server;/81 default_server;/" /etc/nginx/sites-enabled/default + +chmod u+x ../ssl.sh +if [[ $isSSL == "false" ]]; then + ../ssl.sh nonssl +else + ../ssl.sh certif + ../ssl.sh ssl +fi + +echo -e "${c_yellow} === Mise en place des scripts et crons ... ===$c_" +[[ ! -e /opt/scripts ]] && mkdir /opt/scripts +cp nc_optimize.sh /opt/scripts/ +cp upgrade.sh /opt/scripts/ +cp occ /opt/scripts/ +echo "alias occ='/opt/scripts/occ'" >> ~/.bashrc +alias occ='/opt/scripts/occ' + +[[ -z $(crontab -l | grep "/var/www/nextcloud/cron.php") ]] && (crontab -l ; echo "*/5 * * * * sudo -u www-data /usr/bin/php -f /var/www/nextcloud/cron.php > /dev/null 2>&1") | crontab -u root - || echo "cron nextcloud ever set, skip" +[[ -z $(crontab -l | grep "/opt/scripts/optimize.sh") ]] && (crontab -l ; echo "5 1 * * * /opt/scripts/optimize.sh > /dev/null 2>&1") | crontab -u root - || echo "cron optimize ever set, skip" +sudo -u www-data php /var/www/nextcloud/occ background:cron +sudo -u www-data php /var/www/nextcloud/occ db:add-missing-indices +sudo -u www-data php /var/www/nextcloud/occ db:convert-filecache-bigint + +echo -e "${c_yellow} === Restarting services ... ===$c_" +service php$PHPVERSION-fpm restart && service nginx restart && service mysql restart && service redis-server restart || (echo -e "${c_red}Impossible de reloader les service$c_" && exit 1) + +bash /opt/scripts/nc_optimize.sh +usermod -aG www-data $YOU + +echo -e "${c_green}Nextcloud a été installé avec succès !\nOuverture...$c_" +URL="http://$nc_domain:$nc_port" +[[ -x $BROWSER ]] && su -c "exec \"$BROWSER\" \"$URL\"" $YOU > /dev/null +path=$(which xdg-open || which gnome-open) && su -c "exec \"$path\" \"$URL\"" $YOU > /dev/null +echo -e "${c_yellow}Can't find browser$c_" diff --git a/.install/nextcloud/ssl.sh b/.install/nextcloud/ssl.sh new file mode 100755 index 0000000..8e9521a --- /dev/null +++ b/.install/nextcloud/ssl.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +if [ "$EUID" -ne 0 ] + then echo "Veuillez executez ce script en root" + exit 1 +fi + +domain=$(cat /etc/nginx/conf.d/nextcloud.conf | grep server_name | awk '{ print $2 }') +domain=$(echo ${domain::-1}) + +[[ ! $1 =~ ^(ssl|nonssl|certif)$ ]] && echo "Veuillez choisir ssl, nonssl ou certif pour créer un certificat ssl" && exit 1 + +ssl(){ + sed -i "s/'overwriteprotocol' => 'http'/'overwriteprotocol' => 'https'/" /var/www/nextcloud/config/config.php + sed -i "s/http/https/" /etc/nginx/conf.d/nextcloud.conf + sed -i "s/fastcgi_param HTTPS off/fastcgi_param HTTPS on/" /etc/nginx/conf.d/nextcloud.conf + sed -i "s/listen 443;/listen 443 ssl;/" /etc/nginx/conf.d/nextcloud.conf + [[ ! -e /etc/nginx/includes ]] && mkdir /etc/nginx/includes + cp .install_templates/ssl.conf /etc/nginx/includes/ + sed -i "/fastcgi_hide_header X-Powered-By;/a \ include includes/ssl.conf;\n ssl_certificate /etc/letsencrypt/live/$domain/fullchain.pem;\n ssl_certificate_key /etc/letsencrypt/live/$domain/privkey.pem;" /etc/nginx/conf.d/nextcloud.conf +} + +nonssl(){ + sed -i "s/'overwriteprotocol' => 'https'/'overwriteprotocol' => 'http'/" /var/www/nextcloud/config/config.php + sed -i "s/https/http/" /etc/nginx/conf.d/nextcloud.conf + sed -i "s/fastcgi_param HTTPS on/fastcgi_param HTTPS off/" /etc/nginx/conf.d/nextcloud.conf + sed -i '/ssl.conf;/d' /etc/nginx/conf.d/nextcloud.conf + sed -i '/ssl_certificate/d' /etc/nginx/conf.d/nextcloud.conf +} + + +install_certbot(){ + sudo apt update + if [[ $(grep buster /etc/os-release) ]]; then + [[ -z $(cat /etc/apt/sources.list | grep "buster-backports main") ]] && echo "deb http://deb.debian.org/debian buster-backports main" >> /etc/apt/sources.list + sudo apt install certbot python-certbot-nginx -t buster-backports -y + elif [[ $(grep stretch /etc/os-release) ]]; then + sudo apt install certbot python-certbot-nginx -y + elif [[ $(grep -E '16.|17.|18.|19.' /etc/os-release) ]]; then + sudo apt install software-properties-common + sudo add-apt-repository universe + sudo add-apt-repository ppa:certbot/certbot + sudo apt update + sudo apt install certbot python-certbot-nginx + else + echo "OS non supporté pour certbot." && exit 1 + fi +} + +create_certificate() { + cd .install_templates + certbot --nginx certonly --non-interactive --agree-tos -m $USER@$domain -d $domain && echo "Le certificat de $domain a bien été déployé" || echo "Une erreur s'est produite lors de la création du certificat SSL" + + ## Cronification + [[ ! -e /opt/scripts ]] && mkdir /opt/scripts + cp ssl_renew.sh /opt/scripts/ + [[ -z $(crontab -l | grep "/opt/scripts/ssl_renew.sh") ]] && (crontab -l ; echo "12 2 * * 1 /opt/scripts/ssl_renew.sh") | crontab -u root - +} + +certif() { + [[ -z $(which certbot) ]] && install_certbot + [[ -n /etc/letsencrypt/live/$domain/fullchain.pem ]] && create_certificate +} + +$@ + +service nginx reload + +exit 0 diff --git a/.install/nextcloud/templates/config_complete.php b/.install/nextcloud/templates/config_complete.php new file mode 100644 index 0000000..db825eb --- /dev/null +++ b/.install/nextcloud/templates/config_complete.php @@ -0,0 +1,52 @@ +'activity_expire_days' => 14, +'auth.bruteforce.protection.enabled' => true, +'blacklisted_files' => +array ( +0 => '.htaccess', +1 => 'Thumbs.db', +2 => 'thumbs.db', +), +'cron_log' => true, +'enable_previews' => true, +'enabledPreviewProviders' => +array ( +0 => 'OC\\Preview\\PNG', +1 => 'OC\\Preview\\JPEG', +2 => 'OC\\Preview\\GIF', +3 => 'OC\\Preview\\BMP', +4 => 'OC\\Preview\\XBitmap', +5 => 'OC\\Preview\\Movie', +6 => 'OC\\Preview\\PDF', +7 => 'OC\\Preview\\MP3', +8 => 'OC\\Preview\\TXT', +9 => 'OC\\Preview\\MarkDown', +), +'filesystem_check_changes' => 0, +'filelocking.enabled' => 'true', +'htaccess.RewriteBase' => '/', +'integrity.check.disabled' => false, +'knowledgebaseenabled' => false, +'logfile' => '/var/log/nextcloud.log', +'loglevel' => 2, +'logtimezone' => 'Europe/Paris', +'log_rotate_size' => 104857600, +'maintenance' => false, +'memcache.local' => '\\OC\\Memcache\\APCu', +'memcache.locking' => '\\OC\\Memcache\\Redis', +'overwriteprotocol' => 'https', +'preview_max_x' => 1024, +'preview_max_y' => 768, +'preview_max_scale_factor' => 1, +'redis' => +array ( +'host' => '/var/run/redis/redis-server.sock', +'port' => 0, +'timeout' => 0.0, +), +'quota_include_external_storage' => false, +'share_folder' => '/Shares', +'skeletondirectory' => '', +'theme' => '', +'trashbin_retention_obligation' => 'auto, 7', +'updater.release.channel' => 'stable', +); diff --git a/.install/nextcloud/templates/configure_php.sh b/.install/nextcloud/templates/configure_php.sh new file mode 100755 index 0000000..68ba77d --- /dev/null +++ b/.install/nextcloud/templates/configure_php.sh @@ -0,0 +1,50 @@ +cp /etc/php/$PHPVERSION/fpm/pool.d/www.conf /etc/php/$PHPVERSION/fpm/pool.d/www.conf.bak +cp /etc/php/$PHPVERSION/cli/php.ini /etc/php/$PHPVERSION/cli/php.ini.bak +cp /etc/php/$PHPVERSION/fpm/php.ini /etc/php/$PHPVERSION/fpm/php.ini.bak +cp /etc/php/$PHPVERSION/fpm/php-fpm.conf /etc/php/$PHPVERSION/fpm/php-fpm.conf.bak +sed -i "s/;env\[HOSTNAME\] = /env[HOSTNAME] = /" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/;env\[TMP\] = /env[TMP] = /" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/;env\[TMPDIR\] = /env[TMPDIR] = /" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/;env\[TEMP\] = /env[TEMP] = /" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/;env\[PATH\] = /env[PATH] = /" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/pm.max_children = .*/pm.max_children = 240/" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/pm.start_servers = .*/pm.start_servers = 20/" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/pm.min_spare_servers = .*/pm.min_spare_servers = 10/" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/pm.max_spare_servers = .*/pm.max_spare_servers = 20/" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/;pm.max_requests = 500/pm.max_requests = 500/" /etc/php/$PHPVERSION/fpm/pool.d/www.conf +sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/$PHPVERSION/cli/php.ini +sed -i "s/;date.timezone.*/date.timezone = Europe\/\Paris/" /etc/php/$PHPVERSION/cli/php.ini +## sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/$PHPVERSION/cli/php.ini # Bug if not using ssl +sed -i "s/memory_limit = 128M/memory_limit = 512M/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/output_buffering =.*/output_buffering = 'Off'/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/max_execution_time =.*/max_execution_time = 1800/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/max_input_time =.*/max_input_time = 3600/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/post_max_size =.*/post_max_size = 10240M/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/upload_max_filesize =.*/upload_max_filesize = 10240M/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/max_file_uploads =.*/max_file_uploads = 100/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;date.timezone.*/date.timezone = Europe\/\Paris/" /etc/php/$PHPVERSION/fpm/php.ini +## sed -i "s/;session.cookie_secure.*/session.cookie_secure = True/" /etc/php/$PHPVERSION/fpm/php.ini # Bug if not using ssl +sed -i "s/;opcache.enable=.*/opcache.enable=1/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.enable_cli=.*/opcache.enable_cli=1/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.memory_consumption=.*/opcache.memory_consumption=128/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.interned_strings_buffer=.*/opcache.interned_strings_buffer=8/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.max_accelerated_files=.*/opcache.max_accelerated_files=10000/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.revalidate_freq=.*/opcache.revalidate_freq=1/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;opcache.save_comments=.*/opcache.save_comments=1/" /etc/php/$PHPVERSION/fpm/php.ini +sed -i "s/;emergency_restart_threshold =.*/emergency_restart_threshold = 10/" /etc/php/$PHPVERSION/fpm/php-fpm.conf +sed -i "s/;emergency_restart_interval =.*/emergency_restart_interval = 1m/" /etc/php/$PHPVERSION/fpm/php-fpm.conf +sed -i "s/;process_control_timeout =.*/process_control_timeout = 10s/" /etc/php/$PHPVERSION/fpm/php-fpm.conf +sed -i "s/09,39.*/# &/" /etc/cron.d/php +(crontab -l ; echo "09,39 * * * * /usr/lib/php/sessionclean 2>&1") | crontab -u root - +cp /etc/ImageMagick-6/policy.xml /etc/ImageMagick-6/policy.xml.bak +sed -i "s/rights\=\"none\" pattern\=\"PS\"/rights\=\"read\|write\" pattern\=\"PS\"/" /etc/ImageMagick-6/policy.xml +sed -i "s/rights\=\"none\" pattern\=\"EPI\"/rights\=\"read\|write\" pattern\=\"EPI\"/" /etc/ImageMagick-6/policy.xml +sed -i "s/rights\=\"none\" pattern\=\"PDF\"/rights\=\"read\|write\" pattern\=\"PDF\"/" /etc/ImageMagick-6/policy.xml +sed -i "s/rights\=\"none\" pattern\=\"XPS\"/rights\=\"read\|write\" pattern\=\"XPS\"/" /etc/ImageMagick-6/policy.xml + +service php$PHPVERSION-fpm restart && service nginx restart diff --git a/.install/nextcloud/templates/my.cnf b/.install/nextcloud/templates/my.cnf new file mode 100644 index 0000000..78e41dd --- /dev/null +++ b/.install/nextcloud/templates/my.cnf @@ -0,0 +1,79 @@ +[client] +default-character-set = utf8mb4 +port = 3306 +socket = /var/run/mysqld/mysqld.sock + +[mysqld_safe] +log_error=/var/log/mysql/mysql_error.log +nice = 0 +socket = /var/run/mysqld/mysqld.sock + +[mysqld] +basedir = /usr +bind-address = 127.0.0.1 +binlog_format = ROW +bulk_insert_buffer_size = 16M +character-set-server = utf8mb4 +collation-server = utf8mb4_general_ci +concurrent_insert = 2 +connect_timeout = 5 +datadir = /var/lib/mysql +default_storage_engine = InnoDB +expire_logs_days = 10 +general_log_file = /var/log/mysql/mysql.log +general_log = 0 +innodb_buffer_pool_size = 1024M +innodb_buffer_pool_instances = 1 +innodb_flush_log_at_trx_commit = 2 +innodb_log_buffer_size = 32M +innodb_max_dirty_pages_pct = 90 +innodb_large_prefix = on +innodb_file_format = barracuda +innodb_file_per_table = 1 +innodb_open_files = 400 +innodb_io_capacity = 4000 +innodb_flush_method = O_DIRECT +key_buffer_size = 128M +lc_messages_dir = /usr/share/mysql +lc_messages = en_US +log_bin = /var/log/mysql/mariadb-bin +log_bin_index = /var/log/mysql/mariadb-bin.index +log_error=/var/log/mysql/mysql_error.log +log_slow_verbosity = query_plan +log_warnings = 2 +long_query_time = 1 +max_allowed_packet = 16M +max_binlog_size = 100M +max_connections = 200 +max_heap_table_size = 64M +myisam_recover_options = BACKUP +myisam_sort_buffer_size = 512M +port = 3306 +pid-file = /var/run/mysqld/mysqld.pid +query_cache_limit = 2M +query_cache_size = 64M +query_cache_type = 1 +query_cache_min_res_unit = 2k +read_buffer_size = 2M +read_rnd_buffer_size = 1M +skip-external-locking +skip-name-resolve +slow_query_log_file = /var/log/mysql/mariadb-slow.log +slow-query-log = 1 +socket = /var/run/mysqld/mysqld.sock +sort_buffer_size = 4M +table_open_cache = 400 +thread_cache_size = 128 +tmp_table_size = 64M +tmpdir = /tmp +transaction_isolation = READ-COMMITTED +user = mysql +wait_timeout = 600 + +[mysqldump] +max_allowed_packet = 16M +quick +quote-names + +[isamchk] +key_buffer = 16M diff --git a/.install/nextcloud/templates/nc_optimize.sh b/.install/nextcloud/templates/nc_optimize.sh new file mode 100755 index 0000000..4a52a92 --- /dev/null +++ b/.install/nextcloud/templates/nc_optimize.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +redis-cli -s /var/run/redis/redis-server.sock <> $log +echo "#################################### $date ####################################" >> $log +echo "####################################################################################" >> $log + +echo "$renew" >> $log + +if [[ $renew = *"No hooks were run"* ]]; then + echo "Rien n'a été fait" >> $log +else + sleep 5 + /etc/init.d/nginx stop + sleep 1 + killall nginx + sleep 3 + /etc/init.d/nginx restart &>> $log + echo "Des certificats ont été renouvellés" >> $log +fi + +exit 0 diff --git a/.install/nextcloud/templates/upgrade.sh b/.install/nextcloud/templates/upgrade.sh new file mode 100755 index 0000000..959386f --- /dev/null +++ b/.install/nextcloud/templates/upgrade.sh @@ -0,0 +1,15 @@ +#!/bin/bash + +/usr/sbin/service nginx stop +sudo -u www-data php /var/www/nextcloud/updater/updater.phar +sudo -u www-data php /var/www/nextcloud/occ status +sudo -u www-data php /var/www/nextcloud/occ -V +sudo -u www-data php /var/www/nextcloud/occ db:add-missing-indices +sudo -u www-data php /var/www/nextcloud/occ db:convert-filecache-bigint +sudo -u www-data sed -i "s/output_buffering=.*/output_buffering=O/" /var/www/nextcloud/.user.ini +sudo -u www-data php /var/www/nextcloud/occ update:check +sudo -u www-data php /var/www/nextcloud/occ app:update --all +/usr/sbin/service php7.3-fpm restart +/usr/sbin/service nginx restart + +exit 0 diff --git a/.install/scuttlebutt.sh b/.install/scuttlebutt.sh new file mode 100755 index 0000000..fa7e5e2 --- /dev/null +++ b/.install/scuttlebutt.sh @@ -0,0 +1,130 @@ +#!/bin/bash +scuttlebutt() { + echo -e "${c_yellow}Onboarding SCUTTLEBUTT...$c_" + where_is_ssb_installed=$(which ssb-server) + where_is_oasis_installed=$(which oasis) + mkdir -p ~/.zen + BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + + if [[ ! $where_is_ssb_installed ]]; then + # KILL the BRUTAL WAY... + kill -9 $(ps auxf --sort=+utime | grep -w ssb-server| grep -v -E 'color=auto|grep' | tail -n 1 | awk '{print $2}') + # Install dependencies + sudo apt-get install -y socat python3-dev libtool python3-setuptools autoconf automake + + # Install nvm + if [[ ! $(which nvm) ]]; then + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash + source ~/.bashrc + export NVM_DIR="$HOME/.nvm" + nvm install --lts + fi + + cd ~/.ssb + + ### Install module + npm install -g sodium-native ssb-backlinks ssb-ws ssb-links ssb-query ssb-secret-blob ssb-private + npm install -g ssb-server + + # TODO plugin activation !?? + # sbot plugins.enable + + ### Install oasis & ssb-cli (could replace ssb-server?? TODO: try it) + npm -g install fraction/oasis#semver: + npm install -g fraction/ssb-daemon + # npm install -g ssb-cli + + # INSTALL sbotc + if [[ ! $(which sbotc) ]]; then + sudo apt install libsodium-dev jq -y + git submodule add https://git.scuttlebot.io/%25133ulDgs%2FoC1DXjoK04vDFy6DgVBB%2FZok15YJmuhD5Q%3D.sha256 sbotc + cd sbotc + make + sudo make install + fi + + fi + + # TEST ssb-server Install + ssbSERVER=$(which ssb-server) + [[ $ssbSERVER == "" ]] && echo "Check your ssb-server install... Cannot find it !!" && exit 1 + # If exists backup ~/.ssb to ~/.ssb_$USER SSB (one time only !) + [[ -d ~/.ssb_$USER ]] && echo "BACKUP already existing... ~/.ssb_$USER !!! Manual check please..." && exit 1 + [[ -d ~/.ssb ]] && [[ ! -d ~/.ssb_$USER ]] && mv ~/.ssb ~/.ssb_$USER + # CREATE ~/.ssb_astroport + [[ ! -d ~/.ssb_astroport ]] && mkdir -p ~/.ssb_astroport && cd ~/.ssb_astroport + # if exists, keep ~/.ssb_$USER/secret* + [[ ! -f ~/.ssb_astroport/secret ]] && [[ -f ~/.ssb_$USER/secret ]] && cp -f ~/.ssb_$USER/secret* ~/.ssb_astroport/ + + # Symlink ~/.ssb -> ~/.ssb_astroport + [[ -L ~/.ssb ]] && rm ~/.ssb + [[ -d ~/.ssb_astroport ]] && ln -s ~/.ssb_astroport ~/.ssb + ## #TODO get ~/.ssb/manifest.json from template + cp ./templates/ssb/manifest.json ~/.ssb/manifest.json + + # Create config (TODO: adapt if public Pub or Local Node) + # TODO: Create unique hostname in swarm !! uidna + + nodename=$(cat /etc/hostname) + extension=$(echo $nodename | cut -d '.' -f 2) + if [[ $extension == $nodename ]]; then + PUB="false" + nodename=$nodename.local + else + PUB="true" + fi + + cat > ~/.ssb/config < ~/.zen/launch-oasis.sh </dev/null | grep -v -E "header|debug symbols" | grep "Network communication" -B1 | head -n1 | awk -F '-' '{ print $1 }') + [[ $libsodium =~ " " ]] && libsodium=$(echo $libsodium | awk '{ print $2 }') + sudo apt install libsodium-dev libssl-dev libffi-dev python3-pip python3-wheel $libsodium -y || err+="Install python3 and $libsodium" + pip3 install duniterpy || err+="Install duniterpy" + pip3 install silkaj --user || err+="Install Silkaj" + + source .profile ## PATH="$HOME/.local/bin:$PATH" +} + + +# Install PHP + MySQL (TODO: REWRITE!!) +php() { + echo -e "${c_yellow}Installation de PHP et MySQL$c_" + if [[ $OS == "buster" ]]; then + sudo apt -y install software-properties-common nginx php php-common php-fpm php-gettext php-gd php-mysql php-curl php-imap php-mbstring php-xml php-cli mariadb-server || err+="Install PHP and MySQL" + elif [[ $OS == "stretch" ]]; then + sudo apt -y install lsb-release apt-transport-https ca-certificates || err+="Install apt-transport-https" + sudo wget -O /etc/apt/trusted.gpg.d/php.gpg https://packages.sury.org/php/apt.gpg || err+="Download PHP key" + echo "deb https://packages.sury.org/php/ $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/php7.3.list + sudo apt update + sudo apt -y install software-properties-common dirmngr nginx php7.3 php7.3-common php7.3-gettext php7.3-fpm php7.3-gd php7.3-mysql php7.3-curl php7.3-imap php7.3-mbstring php7.3-xml php7.3-cli mariadb-server || err+="Install PHP and MySQL" + else + echo "${c_red}Votre système n'est pas pris en charge par ce script d'installation.$c_" + exit 1 + fi + + isLocalhostUsers=$(sudo mysql -e "select user from mysql.user;" | grep "localhost") + isTestDB=$(sudo mysql -e "show databases" | grep "test") + [[ -n $isLocalhostUsers ]] && sudo mysql -e "DROP USER ''@'localhost'; DROP USER ''@'$(hostname)'" + [[ -n $isTestDB ]] && sudo mysql -e "DROP DATABASE test" + sudo mysql -e "FLUSH PRIVILEGES" +} + +# Read arguments +for i in $@; do + echo -e "${c_yellow}Installation de $c_light$i$c_" + $i +done + +if [[ $err ]]; then + echo -e "${c_red}Installation des prérequis incomplète: $err$c_" + exit 1 +else + echo -e "${c_green}Les prérequis ont été correctement installés$c_" + exit 0 +fi diff --git a/.install/ssb-patchfoo/config b/.install/ssb-patchfoo/config new file mode 100644 index 0000000..ce61ea2 --- /dev/null +++ b/.install/ssb-patchfoo/config @@ -0,0 +1,7 @@ +{ + "plugins": { + "ssb-private": true, + "ssb-backlinks": true, + "patchfoo": true + } +} diff --git a/.install/ssb-patchfoo/install b/.install/ssb-patchfoo/install new file mode 100755 index 0000000..18ff427 --- /dev/null +++ b/.install/ssb-patchfoo/install @@ -0,0 +1,65 @@ +#!/usr/bin/env bash +# shellcheck disable=SC1091 +true +VERSION="TO-V0.5" + +BASE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +CURRENT_DIR="$(pwd)" + +# Install node.js shared module +# shellcheck source=../shared/node.js/install +source "$BASE_DIR/../shared/node.js/install" + +# Make folder if service did not +mkdir -p ~/.ssb/node_modules || true + +# shellcheck disable=SC2164 +cd ~/.ssb/node_modules + +# Install dependencies +npm install --unsafe-perm asyncmemo hashlru pull-stream pull-cat multicb hyperscript pull-paramap ssb-contact ssb-sort stream-to-pull-stream emoji-server pull-paginate ssb-mentions busboy mime-types pull-identify-filetype human-time pull-hyperscript jpeg-autorotate pull-catch diff pull-split pull-utf8-decoder ssb-web-resolver highlight.js pull-box-stream base64-url ssb-backlinks ssb-private + +# Install patchfoo and enable plugin +git clone https://github.com/tomeshnet/patchfoo.git patchfoo + +( +# shellcheck disable=SC2164 +cd patchfoo +git checkout ${VERSION} +) + +# Replace ssb-server plugins.install with a static config file +# This will prevent the installation of these modules a second time +# and compiling downlevel for a 3rd time +cp "$BASE_DIR/config" ~/.ssb +#ssb-server plugins.install ssb-private +#ssb-server plugins.install ssb-backlinks +#ssb-server plugins.enable patchfoo + +# Stop ssb service to process plugin +sudo systemctl stop ssb + +# Disable the git-ssb and npm-ssb prerequisite +# Comment out two lines in patchwork that create a prerequisite for git-ssb and npm-ssb +# but don't seem to serve any purpose. git-ssb and and npm-ssbis not available on npm +sed -i 's#var Git#//var Git#' patchfoo/lib/app.js patchfoo/lib/app.js +sed -i 's#this.git = new Git(this.sbot, this.config)#//this.git = new Git(this.sbot, this.config)#' patchfoo/lib/app.js +sed -i 's#var SsbNpmRegistry#//var SsbNpmRegistry#' patchfoo/lib/app.js patchfoo/lib/app.js +sed -i 's#this.serveSsbNpmRegistry = SsbNpmRegistry.respond(this.sbot, this.config)#//this.serveSsbNpmRegistry = SsbNpmRegistry.respond(this.sbot, this.config)#' patchfoo/lib/app.js + +# Comment out line that breaks things +sed -i "s#h('input', {type: 'file', name: 'upload'})#//h('input', {type: 'file', name: 'upload'})#" patchfoo/lib/serve.js + +# Start service again to start patchfoo +sudo systemctl start ssb + +# Install nginx reverse proxy file +sudo cp "$BASE_DIR/ssb-patchfoo.conf" /etc/nginx/site-path-enabled/ssb-patchfoo.conf + +# Add entry into nginx home screen +APP="

Patch Foo

Plain SSB web UI.
Go
" +sudo sed -i "s#<\!--APPLIST-->#$APP\n<\!--APPLIST-->#" "/var/www/html/index.html" + +# shellcheck disable=SC2164 +cd "$CURRENT_DIR" diff --git a/.install/ssb-patchfoo/ssb-patchfoo.conf b/.install/ssb-patchfoo/ssb-patchfoo.conf new file mode 100644 index 0000000..5ca513f --- /dev/null +++ b/.install/ssb-patchfoo/ssb-patchfoo.conf @@ -0,0 +1,9 @@ + location /patchfoo { + proxy_pass http://127.0.0.1:8027/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + sub_filter "=\"/" "=\"/patchfoo/"; + sub_filter_once off; + } diff --git a/.install/ssl.sh b/.install/ssl.sh new file mode 100755 index 0000000..6eadd3f --- /dev/null +++ b/.install/ssl.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized + +### Vars +args=$(echo $@ | tr " " "\n") + +if [[ "$args" =~ "DOMAIN=" ]]; then + DOMAIN=$(echo "$args" | grep "\> $myProfile +done + +# Export for user +[[ -n $(grep "/.zen/ipfs/" ~/.bashrc) ]] && sed -i -n '/\/.zen\/ipfs/!p' ~/.bashrc +[[ -n $(grep "# ASTROPORT " ~/.bashrc) ]] && sed -i -n '/# ASTROPORT /!p' ~/.bashrc +echo -e "# ASTROPORT ENVIRONMENT VARIABLE\n. /home/$YOU/.zen/ipfs/.$ipfsnodeid/profile" >> ~/.bashrc + +# Export for root (obsolete) +#[[ -n $(sudo grep "/.zen/ipfs/" /root/.bashrc) ]] && sudo sed -i -n '/\/.zen\/ipfs/!p' /root/.bashrc +#[[ -n $(sudo grep "# ASTROPORT " /root/.bashrc) ]] && sudo sed -i -n '/# ASTROPORT /!p' /root/.bashrc +#echo -e "# ASTROPORT ENVIRONMENT VARIABLE\n. /home/$YOU/.zen/ipfs/.$ipfsnodeid/profile" | sudo tee -a /root/.bashrc + +[[ $1 != "noexec" ]] && exec bash diff --git a/.install/templates/copylaradio/mpd.conf b/.install/templates/copylaradio/mpd.conf new file mode 100644 index 0000000..e3fc247 --- /dev/null +++ b/.install/templates/copylaradio/mpd.conf @@ -0,0 +1,36 @@ +music_directory "/home/_USER_/.zen/audio" +playlist_directory "/home/_USER_/.zen/playlists" +user "_USER_" +bind_to_address "localhost" +auto_update "yes" +zeroconf_enabled "yes" +zeroconf_name "Astroport CopyLaRadio Jukebox" +connection_timeout "120" +filesystem_charset "UTF-8" +id3v1_encoding "UTF-8" +log_file "syslog" +############################### + + audio_output { + type "pulse" + name "My Pulse Output" + server "127.0.0.1" + } + + audio_output { + type "httpd" + name "CopyLaRadio HTTP Stream" + encoder "lame" + port "8000" + quality "5.0" + # bitrate "128" + format "44100:16:1" + } + + audio_output { + type "fifo" + name "my pipe" + path "/tmp/snapfifo" + format "48000:16:2" + mixer_type "software" + } diff --git a/.install/templates/ipfs/ipfs-initV.sh b/.install/templates/ipfs/ipfs-initV.sh new file mode 100644 index 0000000..1f2f8a8 --- /dev/null +++ b/.install/templates/ipfs/ipfs-initV.sh @@ -0,0 +1,101 @@ +#!/bin/sh +### BEGIN INIT INFO +# Provides: ipfs daemon +# Required-Start: $local_fs $remote_fs $network $syslog $named +# Required-Stop: $local_fs $remote_fs $network $syslog $named +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Starts the ipfs daemon +# Description: Starts the ipfs daemon using the start-stop-daemon +### END INIT INFO + +# Author: Dylan Powers = 3.2-14) to ensure that this file is present +# and status_of_proc is working. +. /lib/lsb/init-functions + +# +# Function that starts the daemon/service +# +do_start() { + # Return + # 0 if daemon has been started + # 1 if daemon was already running + # 2 if daemon could not be started + start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test >/dev/null \ + || return 1 + start-stop-daemon --start --quiet --pidfile $PIDFILE --make-pidfile \ + --background --chuid $IPFS_USER --no-close \ + --exec /usr/bin/env IPFS_PATH="$IPFS_PATH" $DAEMON 2>>/var/log/ipfs.log 1>/dev/null \ + -- $DAEMON_ARGS \ + || return 2 +} + +# +# Function that stops the daemon/service +# +do_stop() { + # Return + # 0 if daemon has been stopped + # 1 if daemon was already stopped + # 2 if daemon could not be stopped + # other if a failure occurred + start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE --name $NAME + RETVAL="$?" + [ "$RETVAL" = 2 ] && return 2 + + # Delete the pid + rm -f $PIDFILE + return "$RETVAL" +} + +case "$1" in + start) + [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME" + do_start + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + stop) + [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME" + do_stop + case "$?" in + 0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;; + 2) [ "$VERBOSE" != no ] && log_end_msg 1 ;; + esac + ;; + status) + status_of_proc "$DAEMON" "$NAME" && exit 0 || exit $? + ;; + restart) + do_stop + do_start + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|status|restart}" >&2 + exit 3 + ;; +esac diff --git a/.install/templates/ipfs/ipfs.service b/.install/templates/ipfs/ipfs.service new file mode 100644 index 0000000..20602e5 --- /dev/null +++ b/.install/templates/ipfs/ipfs.service @@ -0,0 +1,11 @@ +[Unit] +Description=IPFS daemon +After=network.target + +[Service] +User=_USER +ExecStart=/usr/local/bin/ipfs daemon --enable-pubsub-experiment --enable-namesys-pubsub --routing=dhtclient --enable-gc +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/.install/templates/nginx/ipfs.conf b/.install/templates/nginx/ipfs.conf new file mode 100644 index 0000000..fcf70dd --- /dev/null +++ b/.install/templates/nginx/ipfs.conf @@ -0,0 +1,28 @@ +server { + listen 80 default_server; + listen [::]:80 default_server; + + root /var/www/html; + index index.html index.htm index.nginx-debian.html; + + server_name _; + + location /g1billet { + proxy_pass http://127.0.0.1:9980; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + + location /ipfs { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } + + location /ipns { + proxy_pass http://127.0.0.1:8080; + proxy_set_header Host $host; + proxy_set_header X-Forwarded-For $remote_addr; + } +} + diff --git a/.install/templates/nginx/proxy.conf b/.install/templates/nginx/proxy.conf new file mode 100644 index 0000000..2cd4213 --- /dev/null +++ b/.install/templates/nginx/proxy.conf @@ -0,0 +1,14 @@ +proxy_redirect off; +proxy_set_header Host $host; +proxy_set_header X-Real-IP $remote_addr; +proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; +client_max_body_size 100m; +client_body_buffer_size 128k; +client_header_buffer_size 64k; +proxy_connect_timeout 90; +proxy_send_timeout 90; +proxy_read_timeout 90; +proxy_buffer_size 16k; +proxy_buffers 32 16k; +proxy_busy_buffers_size 64k; + diff --git a/.install/templates/ssb/manifest.json b/.install/templates/ssb/manifest.json new file mode 100644 index 0000000..8ed9b88 --- /dev/null +++ b/.install/templates/ssb/manifest.json @@ -0,0 +1,118 @@ +{ + "auth": "async", + "address": "sync", + "manifest": "sync", + "multiserver": { + "parse": "sync", + "address": "sync" + }, + "multiserverNet": {}, + "get": "async", + "createFeedStream": "source", + "createLogStream": "source", + "messagesByType": "source", + "createHistoryStream": "source", + "createUserStream": "source", + "createWriteStream": "sink", + "links": "source", + "add": "async", + "publish": "async", + "getAddress": "sync", + "getLatest": "async", + "latest": "source", + "latestSequence": "async", + "whoami": "sync", + "progress": "sync", + "status": "sync", + "getVectorClock": "async", + "version": "sync", + "help": "sync", + "seq": "async", + "usage": "sync", + "clock": "async", + "plugins": { + "install": "source", + "uninstall": "source", + "enable": "async", + "disable": "async", + "help": "sync" + }, + "gossip": { + "add": "sync", + "remove": "sync", + "connect": "async", + "disconnect": "async", + "changes": "source", + "reconnect": "sync", + "disable": "sync", + "enable": "sync", + "ping": "duplex", + "get": "sync", + "peers": "sync", + "help": "sync" + }, + "replicate": { + "changes": "source", + "upto": "source", + "request": "sync", + "block": "sync" + }, + "friends": { + "hopStream": "source", + "onEdge": "sync", + "isFollowing": "async", + "isBlocking": "async", + "hops": "async", + "help": "sync", + "get": "async", + "createFriendStream": "source", + "stream": "source" + }, + "blobs": { + "get": "source", + "getSlice": "source", + "add": "sink", + "rm": "async", + "ls": "source", + "has": "async", + "size": "async", + "meta": "async", + "want": "async", + "push": "async", + "changes": "source", + "createWants": "source", + "help": "sync" + }, + "backlinks": { + "read": "source" + }, + "invite": { + "create": "async", + "use": "async", + "accept": "async" + }, + "query": { + "read": "source", + "explain": "sync", + "help": "sync" + }, + "search": { + "query": "source", + "help": "sync" + }, + "links2": { + "read": "source" + }, + "ws": {}, + "ebt": { + "replicate": "duplex", + "request": "sync", + "block": "sync", + "peerStatus": "sync" + }, + "ooo": { + "stream": "duplex", + "get": "async", + "help": "sync" + } +} diff --git a/.install/update_bashrc.sh b/.install/update_bashrc.sh new file mode 100755 index 0000000..b8c8c6b --- /dev/null +++ b/.install/update_bashrc.sh @@ -0,0 +1,34 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.12.06 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# update_bashrc.sh +# Add $YOU $IPFSNODEID $G1PUB to ~/.bashrc + +source ~/.bashrc + +[[ $YOU != "" && $IPFSNODEID != "" && $G1PUB != "" ]] && er+="bashrc already updated" + +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" + +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" + +[[ -f ~/.zen/secret.dunikey ]] && G1PUB=$(cat ~/.zen/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) || er+=" ~/.zen/secret.dunikey missing cannot find G1PUB" + +[[ $er != "" ]] && echo "$er" && exit 1 + +echo "### ASTROPORT IDENTITY ###" >> ~/.bashrc +echo "YOU=$YOU" >> ~/.bashrc +echo "G1PUB=$G1PUB" >> ~/.bashrc +echo "IPFSNODEID=$IPFSNODEID" >> ~/.bashrc +## Add jaklis/.env ? +# cat ~/.zen/astroport/zen/jaklis/.env >> ~/.bashrc + +source ~/.bashrc + +echo "UPDATE ~/.bashrc OK" +tail -n 4 ~/.bashrc + +exit 0 diff --git a/.install/youtube-dl.sh b/.install/youtube-dl.sh new file mode 100755 index 0000000..36e9d3a --- /dev/null +++ b/.install/youtube-dl.sh @@ -0,0 +1,8 @@ +#!/bin/bash +######################################################################## +######## YOUTUBE-DL ########## +if [[ ! $(which youtube-dl) ]]; then + sudo wget https://yt-dl.org/downloads/latest/youtube-dl -O /usr/local/bin/youtube-dl || exit 1 + sudo chmod a+rx /usr/local/bin/youtube-dl + sudo chown $USER /usr/local/bin/youtube-dl +fi diff --git a/1stRUNconfig.sh b/1stRUNconfig.sh new file mode 100755 index 0000000..31ccf6a --- /dev/null +++ b/1stRUNconfig.sh @@ -0,0 +1,113 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.12.04 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# +# This script is run from ~/.config/autostart/Astroport_X_config.desktop +# +######################################################################## +# Create secret key files +# ~/.zen/secret.june +# ~/.zen/secret.dunikey +# ~/.zen/secret.ipfs & /tmp/config.ipfs +# ~/.zen/ipfs.sync +######################################################################## +[[ $(which zenity) == "" ]] && echo "Please! sudo apt install zenity. EXIT" && exit 1 +[[ -f ~/.zen/ipfs.sync ]] && echo "CONFIG ALREADY DONE" && source ~/.zen/ipfs.sync && exit 0 + +function cleanTMP +{ + rm -f /tmp/secret.june /tmp/secret.dunikey /tmp/config.ipfs /tmp/secret.ipfs /tmp/secret.all +} + +# NB: 2> >(grep -v 'GtkDialog' >&2) remove zenity console warning +zenity --question --width 300 --text "Initialiser votre station Astroport?" 2> >(grep -v 'GtkDialog' >&2) +[ $? == 1 ] && exit 1 + +######################################################################## +# LOGIN (=SALT) +salt="$(~/.zen/astroport/zen/tools/diceware.sh 3 | xargs)" +# PASS (=PEPPER) +pepper="$(~/.zen/astroport/zen/tools/diceware.sh 3 | xargs)" + +g1_profil=$(zenity --entry --width 300 --text "Nom de votre machine" --title "Astroport -- Profil" --entry-text="$(hostname)" 2> >(grep -v 'GtkDialog' >&2)); +g1_salt=$(zenity --entry --width 300 --text "Identifiant gchange/cesium (sel)" --title "Astroport - Sel" --entry-text="$salt" 2> >(grep -v 'GtkDialog' >&2)); +[ ${#g1_salt} -lt 8 ] && zenity --warning --width 300 --text "Identifiant doit faire plus de 8 caractères!!" 2> >(grep -v 'GtkDialog' >&2) && cleanTMP && exit 1 +g1_pepper=$(zenity --entry --width 300 --text "Mot de passe gchange/cesium (poivre)" --title "Astroport - Poivre" --entry-text="$pepper" 2> >(grep -v 'GtkDialog' >&2)); + + +echo "CREATING /tmp/secret.june" +echo "$g1_salt" > /tmp/secret.june +echo "$g1_pepper" >> /tmp/secret.june + +echo "________________ https://gchange.fr ________________" > /tmp/secret.all +cat /tmp/secret.june >> /tmp/secret.all + +######################################################################## +echo "CREATING /tmp/secret.dunikey" +python3 ~/.zen/astroport/zen/tools/key_create_dunikey.py "$g1_salt" "$g1_pepper" + +g1pub=$(cat /tmp/secret.dunikey | grep "pub" | cut -d ' ' -f 2) +g1sec=$(cat /tmp/secret.dunikey | grep "sec" | cut -d ' ' -f 2) + +echo "" >> /tmp/secret.all +echo "_________________ https://cesium.app _______________" >> /tmp/secret.all +cat /tmp/secret.dunikey >> /tmp/secret.all +echo "" >> /tmp/secret.all + + +######################################################################## +echo "CREATING /tmp/config.ipfs" +ipfs_ID=$(python3 ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py) + +echo $ipfs_ID > /tmp/secret.ipfs && source /tmp/secret.ipfs +jq -r --arg PeerID "$PeerID" '.Identity.PeerID=$PeerID' ~/.ipfs/config > /tmp/config.tmp +jq -r --arg PrivKEY "$PrivKEY" '.Identity.PrivKey=$PrivKEY' /tmp/config.tmp > /tmp/config.ipfs +rm /tmp/config.tmp + +echo "" >> /tmp/secret.all +echo "_____________________ IPFS ________________________" >> /tmp/secret.all +cat /tmp/secret.ipfs >> /tmp/secret.all +echo "" >> /tmp/secret.all + +## Which directory to sync with IPFS +IPFS_sync_directory=$(zenity --file-selection --title="Choisissez le répertoire à partager par IPFS avec vos amis" --directory 2> >(grep -v 'GtkDialog' >&2)) +[[ ! -d $IPFS_sync_directory ]] && zenity --warning --width 300 --text "Aucun répertoire... ANNULATION" 2> >(grep -v 'GtkDialog' >&2) && cleanTMP && exit 1 + +echo "IPFS_SYNC_DIR=$IPFS_sync_directory" >> /tmp/secret.all + +######################################################################## +echo "Copy new keys?" +zenity --width=971 --height=600 --title "Validation de votre configuration" --text-info --filename="/tmp/secret.all" 2> >(grep -v 'GtkDialog' >&2) +[ $? == 1 ] && cleanTMP && exit 1 + +[[ -f ~/.zen/secret.dunikey.old ]] && zenity --warning --width 300 --text "ANNULATION! Il existe déjà une sauvegarde d'anciennes clefs... support@qo-op.com" 2> >(grep -v 'GtkDialog' >&2) && cleanTMP && exit 1 + +mv ~/.zen/secret.june ~/.zen/secret.june.old +mv /tmp/secret.june ~/.zen/secret.june + +mv ~/.zen/secret.dunikey ~/.zen/secret.dunikey.old +mv /tmp/secret.dunikey ~/.zen/secret.dunikey + +mv ~/.zen/secret.ipfs ~/.zen/secret.ipfs.old +mv /tmp/secret.ipfs ~/.zen/secret.ipfs + +mv ~/.ipfs/config ~/.ipfs/config.old +mv /tmp/config.ipfs ~/.ipfs/config + +rm /tmp/secret.all + +# WRITE ~/.zen/ipfs.sync CONFIG +echo "IPFS_SYNC_DIR=$IPFS_sync_directory" > ~/.zen/ipfs.sync + +# CREATE gchange+ profile +# cd ~/.zen/astroport/zen/jaklis +# ./jaklis.py --key "~/.zen/secret.dunikey" --node "https://data.gchange.fr" set --name "$g1_profil" --avatar "~/.zen/astroport/logo.png" +# ERROR + +echo "OK. Restart now" +zenity --warning --width 300 --text "Veuillez redémarrer l'ordinateur pour activer votre configuration..." 2> >(grep -v 'GtkDialog' >&2) + +exit 0 diff --git a/ISOconfig.sh b/ISOconfig.sh new file mode 100755 index 0000000..4a2d15c --- /dev/null +++ b/ISOconfig.sh @@ -0,0 +1,204 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.12.05 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# AUTOMATIC version of interactive 1stRUNconfig.sh +# This script can run from ~/.config/autostart/Astroport_X_config.desktop +# +######################################################################## +# Create secret key files +# ~/.zen/secret.june +# ~/.zen/secret.dunikey +# ~/.zen/secret.ipfs & /tmp/config.ipfs +# ~/.zen/ipfs.sync +######################################################################## + +# CHECK IF CONFIG ALREADY DONE !! Remove ~/.zen/ipfs ~/.zen/secret ~/.zen/ipfs.sync +if [[ -f ~/.zen/ipfs.sync ]]; then + echo "CONFIG ALREADY DONE" + source ~/.zen/ipfs.sync + # NEW BOOT! SEND ipfstryme message to my friends... + cd ~/.zen/astroport/zen/jaklis + # GET LIST of issuer(s) who likes me + for liking_me in $(./jaklis.py like | jq -r '.likes[].issuer'); + do + # CHECk if I am liking him either + friend_of_mine=$(./jaklis.py like -p $liking_me | jq -r '.yours'); + echo "Sending IPFSTRYME message to $liking_me" + [[ $friend_of_mine != null ]] && ./jaklis.py send -d $liking_me -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr + done + cd - + exit 0 +fi + +# CHECK INTERNET CONNECTIVITY !! +ping -q -w 1 -c 1 `ip r | grep default | cut -d ' ' -f 3` > /dev/null && echo ok || (echo "NO INTERNET CONNEXION" && exit 1) + +######################################################################## +######################################################################## +echo "CREATING Gchange credentials" +######################################################################## +salt="$(~/.zen/astroport/zen/tools/diceware.sh 3 | xargs)" +[[ $salt == "" ]] && echo "ERROR" && exit 1 +pepper="$(~/.zen/astroport/zen/tools/diceware.sh 3 | xargs)" + +g1_profil=$(hostname) +g1_salt="$salt" +g1_pepper="$pepper" + +echo "CREATING /tmp/secret.june" +echo "$g1_salt" > /tmp/secret.june +echo "$g1_pepper" >> /tmp/secret.june + +######################################################################## +######################################################################## +echo "CREATING /tmp/secret.dunikey" +######################################################################## +python3 ~/.zen/astroport/zen/tools/key_create_dunikey.py "$g1_salt" "$g1_pepper" + +g1pub=$(cat /tmp/secret.dunikey | grep "pub" | cut -d ' ' -f 2) +g1sec=$(cat /tmp/secret.dunikey | grep "sec" | cut -d ' ' -f 2) + +######################################################################## +######################################################################## +echo "CREATING /tmp/config.ipfs" +######################################################################## +ipfs_ID=$(python3 ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py) +echo $ipfs_ID > /tmp/secret.ipfs && source /tmp/secret.ipfs +[[ $PrivKEY == "" ]] && echo "ERROR" && exit 1 +jq -r --arg PeerID "$PeerID" '.Identity.PeerID=$PeerID' ~/.ipfs/config > /tmp/config.tmp +jq -r --arg PrivKEY "$PrivKEY" '.Identity.PrivKey=$PrivKEY' /tmp/config.tmp > /tmp/config.ipfs +rm /tmp/config.tmp + +# +IPFSNODEID=$PeerID +echo $IPFSNODEID + +## Declare directory transfered in IPFS +IPFS_sync_directory="$HOME/astroport" + +######################################################################## +# INSTALL KEYS +######################################################################## +mv ~/.zen/secret.june ~/.zen/secret.june.old +mv /tmp/secret.june ~/.zen/secret.june + +mv ~/.zen/secret.dunikey ~/.zen/secret.dunikey.old +mv /tmp/secret.dunikey ~/.zen/secret.dunikey + +mv ~/.zen/secret.ipfs ~/.zen/secret.ipfs.old +mv /tmp/secret.ipfs ~/.zen/secret.ipfs + +mv ~/.ipfs/config ~/.ipfs/config.old +mv /tmp/config.ipfs ~/.ipfs/config + +# WRITE ~/.zen/ipfs.sync CONFIG +echo "IPFS_SYNC_DIR=$IPFS_sync_directory" > ~/.zen/ipfs.sync + +######################################################################## +echo "INIT ~/.zen/ipfs/.${IPFSNODEID}" +######################################################################## +rm -Rf ~/.zen/ipfs +mkdir -p ~/.zen/ipfs/.${IPFSNODEID}/G1SSB +######################################################################## +# Give $XZUID to your (gchange friends) to add in Vstream Astroport and access your sharings +# IPNS link to "pastebin list" http://localhost:8080/ipns/$IPNSKEY/xbian/$XZUID +######################################################################## +XZUID=$(~/.zen/astroport/zen/tools/diceware.sh 1 | xargs)-$(~/.zen/astroport/zen/tools/diceware.sh 1 | xargs)$(hostname -I | cut -d ' ' -f 1 | cut -d "." -f 4 ) +echo "CREATE ~/.zen/ipfs/xbian/$XZUID" +touch ~/.zen/ipfs/xbian/$XZUID +# https://github.com/Kodi-vStream/venom-xbmc-addons/wiki/Voir-et-partager-sa-biblioth%C3%A8que-priv%C3%A9e#d%C3%A9clarer-des-films +echo "$XZUID" > ~/.zen/ipfs/.${IPFSNODEID}/_xbian.zuid + +######################################################################## +######################################################################## +echo "CREATE gchange+ profile" +######################################################################## +cd ~/.zen/astroport/zen/jaklis +./jaklis.py set --name "$XZUID" --avatar "$HOME/.zen/astroport/logo.png" + +######################################################################## +######################################################################## +echo "BECOME FRIEND with oasis (1st bootstrap)" +######################################################################## +./jaklis.py like -p 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS -s 5 + +######################################################################## +echo "RESTART ipfs" +######################################################################## +sudo service ipfs restart +echo ".... WAIT for SWARM to connect ..." +sleep 10 + +ipfs swarm peers + +######################################################################## +echo 'EXTEND ~/.bashrc' +######################################################################## +~/.zen/astroport/.install/update_bashrc.sh + +echo "Configure .kodi/addons/plugin.video.vstream/resources/sites/astroport.py" +cp -f ~/.zen/astroport/.install/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py \ +~/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py + +# CREATE xbian IPNSKEY used to publish ~/.zen/ipfs_swarm/xbian (contains all XZUID pastebin files) +ipfs key rm xbian +IPNSKEY=$(ipfs key gen xbian) +sed -i s/_IPNSKEY_/$IPNSKEY/g ~/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py +sed -i s/_PROFIL_/$XZUID/g ~/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py +sed -i s/_LOGIN_/$salt/g ~/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py +sed -i s/_MDP_/$pepper/g ~/.kodi/addons/plugin.video.vstream/resources/sites/astroport.py + +~/.zen/astroport/zen/gchange_IPFS_swarm.sh +~/.zen/astroport/zen/ipfs_SWARM_refresh.sh + +# Optionnal PUBLISH actual (later done by cron_MINUTE.sh) +IXBIAN=$(ipfs add -qr ~/.zen/ipfs_swarm/xbian | tail -n 1) +JXBIAN=$(ipfs name publish -k xbian $I) + +######################################################################## +echo 'SEND "ipfstryme" message' +######################################################################## +echo "" > ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr # ERASE +for tryme in $(ipfs id | jq -r .Addresses[]); +do + isLAN=$(echo $tryme | cut -f3 -d '/' | grep -E "(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + [[ ! $isLAN && $tryme != "" ]] && echo "$tryme" >> ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr && echo "$tryme" +done + +echo 'SEND ipfstryme to oasis' # Add your bootstrap Pub here +./jaklis.py send -d 2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr + +######################################################################## +echo 'INSTALL Astroport cron_MINUTE' +######################################################################## +~/.zen/astroport/zen/tools/cron_VRFY.sh + +# Run only once: comment /etc/rc.local (su - xbian -c "~/.zen/astroport/ISOconfig.sh") +# sudo sed -i s/su/\#su/g /etc/rc.local +# BETTER NOT: Send new 'ipfstryme' message on each boot is done instead + +echo "CHANGE hostname" +sudo sed -i s/astrxbian/$XZUID/g /etc/hostname +sudo sed -i s/astrxbian/$XZUID/g /etc/hosts + +echo "CHANGE DNS" # Avoid provider restrictions +sudo cat > /etc/resolv.conf <1{sum+=$4}END{print sum}') + diskSize="$((availableDiskSize / 2))" + ipfs config Datastore.StorageMax $diskSize +echo ' + _____ __ __ ___ + / ___// /_____ _________ _____ ____ / |/ /___ __ __ + \__ \/ __/ __ \/ ___/ __ `/ __ `/ _ \ / /|_/ / __ `/ |/_/ + ___/ / /_/ /_/ / / / /_/ / /_/ / __/ / / / / /_/ /> < + /____/\__/\____/_/ \__,_/\__, /\___/ /_/ /_/\__,_/_/|_| + /____/ +# 1/2 HDD for IPFS size - dicotomic adaptation 7th EVERY MONTH +# +# TODO: Read parameters from ipfs, publish status to swarm, could be use for youtube-dl or other kind of heavy ipfs input Station election. +# +' + echo "StorageMax = $diskSize" + + fi +################################################################## + + +fi diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..513b93a --- /dev/null +++ b/install.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +{ +# Check requirements +sudo apt update +sudo apt install git curl net-tools -y + +gitpath="https://git.p2p.legal/axiom-team/astroport/raw/master" + +echo "Add colors ..." +curl -s $gitpath/.install/export_colors.sh | bash || exit 1 +[[ -n $(grep ".bash_aliases" ~/.bashrc) ]] && echo ". ~/.bash_aliases" ~/.bashrc +. ~/.bash_aliases + +# Get arguments +args="$@" + +# IPFS install +echo -e "${c_green}Astroport installer +### +1. IPFS Swarm Layer$c_" +# Full automatic (you trust this git depot) +curl -s $gitpath/.install/ipfs_alone.sh | bash || exit 1 + +# Set environment variables +curl -s $gitpath/.install/sys_checkOS.sh noexec | bash || exit 1 + +# SSB install (now using cesium+/gchange+) +# echo -e "${c_green}2. Ḡ1/ScuttleButt anoptical layer$c_" +# ONCE YOU READ and AGREE. Run TrustFull QUICK Install !! +# curl -s $gitpath/zen/tools/make_G1SSB_secret.sh | bash || exit 1 + + +# LOVEland install +curl -s $gitpath/.install/loveland.sh | bash || exit 1 + + + + + +# Customs install +## Nextcloud +[[ $args =~ nextcloud ]] && cd /home/$USER/.zen/astroport && sudo -E bash .install/nextcloud/install.sh + +echo -e "${c_green}Installation complete$c_" + +exec bash + +} diff --git a/zen/README.md b/zen/README.md new file mode 100644 index 0000000..a8c3aee --- /dev/null +++ b/zen/README.md @@ -0,0 +1,129 @@ +# INSIDE ZEN REACTOR + + +``` + ~/.zen # Astroport Metaverse Files + ~/.zen/astroport # CODE + ~/.zen/secret.astroport.key # MULTI UNIVERSE KEY + + ~/.zen/cache/g1_TX_inputs/ # G1 Wallet INPUT Tx cache + ~/.zen/cache/ssb_contact # Data from ./ssb_SURVEY_contact.sh + + ~/.zen/tag/ # CONTAINS "ZenTAG counters" + + la ~/.zen/tag/9c6354a1db832e51ea0acd9342822dc49ba42c7035e830a2d59fcf4f86a13821/ +_chain _g1.node.creator _ipns _passenger.ipfs.crypt _QRCODE.write.png _tag.zen +_chain.n _ipfs.node.creator _passenger.contract.sh _passenger.park _tag.BB.sha _tag.zensource +_chain.nanodate _ipfs.publishkey.BB.aes _passenger.filename _passenger.read _tag.issuer +_chain.prev _ipfs.publishkey.crypt _passenger.ipfs.BB.aes _QRCODE.read.png _tag.uid + + + ~/.zen/miam/ # youtube-dl Stomac + + +# IPFS Shared Data Frameworks + ~/.zen/ipfs + ~/.zen/ipfs_swarm + +# Each "G1SSBIPFS" Node publish ID and DATA indexes + ~/.zen/ipfs_swarm/.12D3KooW***/ +# Other Swarm Node access with "ipfs ls /ipns/Qm***/.12D3KooW***" + + +``` + +> ~/.zen/ipfs_swarm/.12D3KooW*** sub directory is used for exchanging messages between peers + +> cron_MINUTE.sh monitor all LOCAL channels populated/monitored through to ~/.zen/ipfs_swarm + + +# IMPORTANT TODO!! +- timeout.sh CPU problem happen once + + +# MOVE swarm.key +``` +mv ~/.ipfs/swarm.key ~/.ipfs/swarm.key.old +sudo systemctl restart ipfs +``` + +# Problem with ~/.ssb/manifest.json + +``` +# Restart ssb-server +kill -9 $(ps auxf --sort=+utime | grep -w ssb-daemon| grep -v -E 'color=auto|grep' | tail -n 1 | awk '{print $2}'); +kill -9 $(ps auxf --sort=+utime | grep -w ssb-server| grep -v -E 'color=auto|grep' | tail -n 1 | awk '{print $2}'); +kill -9 $(ps auxf --sort=+utime | grep -w oasis | grep -v -E 'color=auto|grep' | tail -n 1 | awk '{print $2}') +printf '{"manifest":"sync"}' > ~/.ssb/manifest.json +ssb-server start & +sbotc -t async manifest > ~/.ssb/manifest.json +``` + + +# New SSB message type + +``` +# ssb_INIT.sh +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_ssb.whoami +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.pubkey +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.qrcode.png +~/.zen/ipfs/.${ipfsnodeid}/Addresses +~/.zen/ipfs/.${ipfsnodeid}/AgentVersion +~/.zen/ipfs/.${ipfsnodeid}/repo.stat +~/.zen/ipfs/.${ipfsnodeid}/tryme.addr +``` + +# IPFS FILESYSTEM STRUCTURE + +``` +# ssb_INIT.sh +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_ssb.whoami +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.pubkey +~/.zen/ipfs/.$ipfsnodeid/G1SSB/_g1.qrcode.png +~/.zen/ipfs/.${ipfsnodeid}/Addresses +~/.zen/ipfs/.${ipfsnodeid}/AgentVersion +~/.zen/ipfs/.${ipfsnodeid}/repo.stat +~/.zen/ipfs/.${ipfsnodeid}/tryme.addr + +# zen_MAKE.sh +~/.zen/ipfs/.$ipfsnodeid/TAG/${J} +~/.zen/ipfs/.$ipfsnodeid/TAG/${J}/_tag.uid +~/.zen/ipfs/.$ipfsnodeid/TAG/${J}/_tag.passenger.filename +~/.zen/ipfs/.$ipfsnodeid/TAG/${J}/_tag.passenger.metadata.json +~/.zen/ipfs/.$ipfsnodeid/TAG/${J}/_tag.passenger.fulltitle + +# ssb_SURVEY_contact.sh +~/.zen/ipfs/.${ipfsnodeid}/CONTACT/${g1author} +~/.zen/ipfs/.${ipfsnodeid}/CONTACT/${g1author}/ipfs_swarm.key.crypt +~/.zen/ipfs/.${ipfsnodeid}/Addresses +~/.zen/ipfs/.${ipfsnodeid}/AgentVersion +~/.zen/ipfs/.${ipfsnodeid}/repo.stat + + +~/.zen/ipfs/.${ipfsnodeid}/CHAN/sha256(id@channel) + + +# ipfs_SWARM_refresh.sh + + +``` + + +# IPFS CHANNELS ARE SYNC FROM LOCAL NODE + +``` +LOCAL: +~/.zen/ipfs/channels/channel_id/_uid + +IPFS SWARM : +~/.zen/ipfs/sha256(id@channel)/_uid +~/.zen/ipfs/sha256(+33647683646@SMS_+33667676767)/_uid + +``` + +# natools CRYPT/DECRYPT a FILE +``` +g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +~/.zen/astroport/zen/tools/natools.py encrypt -p $g1pub -i file -o file.crypt +~/.zen/astroport/zen/tools/natools.py decrypt -f pubsec -k ~/.ssb/secret.dunikey -i file.crypt -o file +``` diff --git a/zen/cesium_INIT.sh b/zen/cesium_INIT.sh new file mode 100644 index 0000000..24058aa --- /dev/null +++ b/zen/cesium_INIT.sh @@ -0,0 +1,116 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.11.30 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! EXIT" && exit 1 +######################################################################## +[[ ! -f ~/.zen/secret.dunikey ]] && echo "Missing ~/.zen/secret.dunikey. EXIT" && exit 1 +G1PUB=$(cat ~/.zen/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +[[ $G1PUB == "" ]] && echo "ERROR G1PUB empty !! EXIT" && exit 1 +######################################################################## + +# GET NODE disk performance. TODO, publish and use as IPFS repartition +echo "DISK SIZE AVAILABLE & PERFORMANCE TESTING" +[[ -f ~/.ipfs/test.disk ]] && rm -f ~/.ipfs/test.disk +diskperf=$(dd if=/dev/zero of=~/.ipfs/test.disk bs=10M count=1 oflag=dsync 2>&1 | tail -n 1 | sed s/\,\ /\ -/g | cut -d '-' -f 4) +# echo $diskperf + +# IPFS LOCAL REPOSITORY for Node Identity +mkdir -p ~/.zen/ipfs/.$IPFSNODEID/G1SSB + +# find IPFSTRYME public address +echo "" > ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +for tryme in $(ipfs id | jq -r .Addresses[]); +do + isLAN=$(echo $tryme | cut -f3 -d '/' | grep -E "(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + [[ ! $isLAN ]] && echo "$tryme" >> ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +done +cat ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr + + +################################ +# ADD Cesium+ informations +CESIUMPLUS="https://g1.data.le-sou.org" + +# PREPARE title +# Made from Gchange+ profile tittle and city OR user@hostname +title=$(curl -s ${CESIUMPLUS}/user/profile/${G1PUB} | jq -r '._source.title') +[[ -f ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && uidna=$(cat ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +# Put in .$IPFSNODEID INDEX: _g1.uidna & _g1.cesium_name (used by Minetest flavour and others) +[[ $uidna ]] && echo "$uidna" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.uidna +[[ $title ]] && echo "$title" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.gchange_name + +[[ $uidna ]] && [[ "$title" == "null" ]] && title="Station $uidna" +[[ "$title" == "null" ]] && title="Station $USER@$(cat /etc/hostname)" + +city=$(curl -s ${CESIUMPLUS}/user/profile/${G1PUB} | jq -r '._source.city') +[[ "$city" != "null" ]] && title="$title in $city" + + +# ADD "cesium_geoPoint.lat" AND "cesium_geoPoint.lon" messages in SSB feed +# This way any SSB account is connected to its Cesium+ Geolocalisation. +geopointlat=$(curl -s ${CESIUMPLUS}/user/profile/${G1PUB} | jq '._source.geoPoint.lat') +geopointlon=$(curl -s ${CESIUMPLUS}/user/profile/${G1PUB} | jq '._source.geoPoint.lon') + +# REFRESH Cesium+ Avatar image +curl -s ${CESIUMPLUS}/user/profile/${G1PUB} | jq -r '._source.avatar._content' | base64 -d > "/tmp/_gchange.avatar.png" + +# Get nodename +[[ -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename ]] && nodename=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename) +if [[ $nodename == "" ]]; then + nodename=$(cat /etc/hostname) + extension=$(echo $nodename | cut -d '.' -f 2) + if [[ $extension == $nodename ]]; then + nodename=$nodename.home + fi + echo "$nodename" > /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename +fi +######################################################################## +# DUNITER G1 Wallet balance +export LC_ALL=C.UTF-8 #attipix +export LANG=C.UTF-8 #attipix + +# COPY NODE G1SSB ID to IPFS +echo "$G1PUB" > ~/.zen/ipfs/.${IPFSNODEID}/G1SSB/_g1.pubkey + +# IPFS Node PUBLISH Adresses so Pub can become bootstrap for ${g1author} +ipfs id | jq -r .Addresses[] > ~/.zen/ipfs/.${IPFSNODEID}/Addresses + +# IPFS Node PUBLISH AgentVersion & repo.stat +ipfs id | jq -r .AgentVersion > ~/.zen/ipfs/.${IPFSNODEID}/AgentVersion +ipfs repo stat > ~/.zen/ipfs/.${IPFSNODEID}/repo.stat + +echo "$diskperf" > ~/.zen/ipfs/.${IPFSNODEID}/disk.perf +echo $(df ~/.ipfs/ | tail -n 1 | awk '{print $4}') > ~/.zen/ipfs/.${IPFSNODEID}/disk.bytes + + +IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --allow-offline --quieter /ipfs/$IWALLETS) + +### +# GET ALL MY CESIUMPLUS FRIENDS AND SEND THEM my IPFS Address + +cd ~/.zen/astroport/zen/jaklis +# GET LIST of issuer(s) who likes me +for liking_me in $(./jaklis.py like | jq -r '.likes[].issuer'); +do + # CHECk if I am liking him either + friend_of_mine=$(./jaklis.py like -p $liking_me | jq -r '.yours'); + echo "Sending IPFSTRYME message to $liking_me" + [[ $friend_of_mine != null ]] && ./jaklis.py send -d $liking_me -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +done +cd - + +exit 0 diff --git a/zen/cron_VRFY.sh b/zen/cron_VRFY.sh new file mode 100755 index 0000000..ae556bc --- /dev/null +++ b/zen/cron_VRFY.sh @@ -0,0 +1,34 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.21 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# VERIFY CRON for cron_MINUTE.sh and ACTIVATE it +########################################################################' +# Get crontab +crontab -l > /tmp/mycron +# Remove any previous line containing "cron_MINUTE" +awk -i inplace -v rmv="cron_MINUTE" '!index($0,rmv)' /tmp/mycron && echo "Astroport cron was there" +# DOUBLE CHECK (awk = nawk or gawk -i ?) +crontest=$(cat /tmp/mycron | grep -F 'cron_MINUTE') +# ADD cron_MINUTE.sh TO cron ? +if [[ ! $crontest ]]; then + echo "* * * * * $MY_PATH/../cron_MINUTE.sh >> /tmp/astroport.log 2>&1>/dev/null" >> /tmp/mycron && crontab /tmp/mycron \ +else + echo "No operation needed. Your crontab is: " && crontab -l +fi +# Clean +rm -f /tmp/mycron + +exit 0 diff --git a/zen/g1_MONITOR_zen.sh b/zen/g1_MONITOR_zen.sh new file mode 100755 index 0000000..0288a6f --- /dev/null +++ b/zen/g1_MONITOR_zen.sh @@ -0,0 +1,189 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 1.0 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# \\/// +# qo-op +############# $ME DELAY +######################################################################## +echo " + _(_)_ _(_)_ + @@@@ (_)@(_) @@@@ (_)@(_) @@@@ + @@()@@ wWWWw (_)\ @@()@@ wWWWw (_)\ @@()@@ wWWWw + @@@@ (___) \|/ @@@@ (___) \|/ @@@@ (___) + / Y \| / Y \| / Y + \ | \ |/ | / \ | \ |/ | / \ | \ |/ + \\|// \\|/// \\\|// \\|// \\|/// \\\|// \\|// \\|/// +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# [ASTROPORT](https://astroport.com) +# ex: ./$ME \"10 days\" +# Survey Node G1 Wallet for TX/IN Commands in received comments ... +######################################################################## +" +DELAY="$1" + +ipfsnodeid=$(ipfs id -f='\n') +[[ "$ipfsnodeid" == "" ]] && echo "$USER Please Install IPFS !!" && exit 1 +[[ ! -d ~/.zen/cache/g1_TX_inputs ]] && mkdir -p ~/.zen/cache/g1_TX_inputs # Directory containing G1 blockchain incoming TX Scan +[[ ! -d ~/.zen/ipfs ]] && mkdir -p ~/.zen/ipfs # Directory where NODE store SMS & other Channel Wallets +[[ ! -d ~/.zen/ipfs_swarm ]] && mkdir -p ~/.zen/ipfs_swarm # Local copy of all SSB friends IPFS Nodes published ipfs + +######################################################################################################### +# GET G1 PUBKEY FROM SCUTTLEBUTT or $2 (DEBUG) +[[ $DELAY == "" ]] && DELAY="60 days" && echo "*** HELP ***" && echo "./$ME \"duration\" (default $DELAY)" && echo "************" && echo +DELAYUNIT=$(echo $DELAY | cut -d ' ' -f 2) + +# g1pub=$2 # DEBUG +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +[[ "$g1pub" == "" ]] && echo "$USER Missing ~/.ssb/secret : Please Install Scuttlebutt server !" && exit 1; + +######################################################################################################### +echo "=======================================" +echo "IPFS Node ID = $ipfsnodeid + __ + _|_ _ _| |\ | _ _| _ /__/| _.|| __|_ +(_| |(/_(_|< | \|(_)(_|(/_ \_| | \/\/(_|||(/_|_ +last $DELAY + +G1 PUBKEY = $g1pub + +" + +# PREPARE TIMESTAMP LIMITS +TIMEBEFORE=$(date -u --date="-$DELAY" +"%s") +TIMESTAMP=$(date -u +"%s") +# GET DUNITER SERVER +DUNITERNODE=$($MY_PATH/tools/duniter_getnode.sh) +DUNITERURL="https://$DUNITERNODE" +# GET BLOCKCHAIN TX FOR TIME WINDOW +curl -s $DUNITERURL/tx/history/$g1pub/times/$TIMEBEFORE/$TIMESTAMP > /tmp/g1_TX.scan.txt +TXNB=$(cat /tmp/g1_TX.scan.txt | wc -l) + +echo " +$DUNITERURL/tx/history/$g1pub/times/$TIMEBEFORE/$TIMESTAMP + ___ + |\/ ._ ._ _ |_ _ ._ +G1 |/\ | ||_|| | ||_)(/_| +$TXNB + +" + +# Choose between "history.received" OR "history.pending" depending on DELAY +if [[ $DELAYUNIT != "minutes" && $DELAYUNIT != "minute" ]]; then + + # HOW MANY TX DO WE HAVE... + TXnb=$(cat /tmp/g1_TX.scan.txt | jq '.history.received[].hash' | wc -l) + echo "During $DELAY, detected $TXnb TX... Now looking for INPUT TX..." + # PARSE $TXnb TX + line=1 + while [[ $line -le $TXnb ]]; do + # GET TX HASH + TXhash=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.received[].hash' | head -n $line | tail -n 1 ) + # IT IS A NEW TX? + if [[ ! -f ~/.zen/cache/g1_TX_inputs/zen.$TXhash ]]; then + # GET LAST TX ISSUER + ISSUER=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.received[].issuers[0]' | head -n $line | tail -n 1) + [[ $ISSUER == $g1pub ]] && ((line++)) && continue ## TX/OUT CONTINUE + echo $ISSUER > ~/.zen/cache/g1_TX_inputs/zen.$TXhash + # GET OUTPUTS AND MAKE ZEN=G1*100 SUM + ZEN=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.received[].outputs[]' | grep $g1pub | head -n $line | tail -n 1 | cut -d ':' -f 1) + # GET COMMENT + COMMENT=$(cat /tmp/g1_TX.scan.txt | jq '.history.received' | jq -r '.[].comment' | head -n $line | tail -n 1) + # echo "TX-IN $ISSUER => $g1pub" + echo "Received $ZEN Zen From $ISSUER :: $COMMENT :: ($TXhash)" + CMD=$(echo "$COMMENT" | awk '{print toupper($1)}') + +echo " +___ ___ _ ___ _ + |\/ | |\ ||_)| || |_) _ _ _ o _ _| + |/\ _|_| \|| |_|| | \(/_(_(/_|\/(/_(_| + +" + + case "$CMD" in + + ZEN) + # Create ZenTAG QRCode + $MY_PATH/zen_MAKE.sh $ZEN $TXhash + ;; + + *) + echo "$TXhash = $ZEN ZEN ($COMMENT)" + # $MY_PATH/zen_MAKE.sh "ME" $TXhash + ;; + + esac + + fi + ((line++)) + done + + +else + + + # MINUTE DELAY = pending // TODO: if anyone succeed in passing $VAR in jq command, both can be merge... + # HOW MANY TX DO WE HAVE... + TXnb=$(cat /tmp/g1_TX.scan.txt | jq '.history.pending[].hash' | wc -l) + echo "During $DELAY, detected $TXnb TX... Now looking for INPUT TX..." + # PARSE $TXnb TX + line=1 + while [[ $line -le $TXnb ]]; do + TXhash=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.pending[].hash' | head -n $line | tail -n 1) + # IT IS A NEW TX/IN? + if [[ ! -f ~/.zen/cache/g1_TX_inputs/zen.$TXhash ]]; then + # GET LAST RX ISSUER + ISSUER=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.pending[].issuers[0]' | head -n $line | tail -n 1) + [[ $ISSUER == $g1pub ]] && ((line++)) && continue ## TX/OUT CONTINUE + echo $ISSUER > ~/.zen/cache/g1_TX_inputs/zen.$TXhash + # GET OUTPUTS AND MAKE ZEN SUM + ZEN=$(cat /tmp/g1_TX.scan.txt | jq -r '.history.pending[].outputs[]' | grep $g1pub | head -n $line | tail -n 1 | cut -d ':' -f 1) + # GET COMMENT + COMMENT=$(cat /tmp/g1_TX.scan.txt | jq '.history.pending' | jq -r '.[].comment' | head -n $line | tail -n 1) + # echo "TX-IN $ISSUER => $g1pub" + echo "Pending $ZEN Zen From $ISSUER :: $COMMENT :: ($TXhash)" + CMD=$(echo "$COMMENT" | awk '{print toupper($1)}') + +echo " +___ ___ _ ___ _ + |\/ | |\ ||_)| || |_)_ ._ _|o._ _ + |/\ _|_| \|| |_|| | (/_| |(_||| |(_| + _| +" + + case "$CMD" in + + ZEN) + # Create ZenTAG QRCode + $MY_PATH/zen_MAKE.sh $ZEN $TXhash + ;; + + *) + echo "$TXhash = $ZEN ZEN ($COMMENT)" + # $MY_PATH/zen_MAKE.sh "ME" $TXhash + ;; + + esac + + fi + ((line++)) + done +fi + +echo "FINISH ! ls ~/.zen/cache/g1_TX_inputs/ + __ +|\ | _ _| _ /__/| \ /_.|| __|_ |_ _.| _.._ _ _ +| \|(_)(_|(/_ \_| | \/\/(_|||(/_|_ |_)(_||(_|| |(_(/_ + +silkaj -p $DUNITERNODE balance $g1pub + +" +exit 0 diff --git a/zen/gchange_INIT.sh b/zen/gchange_INIT.sh new file mode 100755 index 0000000..29e3d72 --- /dev/null +++ b/zen/gchange_INIT.sh @@ -0,0 +1,117 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.11.30 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! EXIT" && exit 1 +######################################################################## +[[ ! -f ~/.zen/secret.dunikey ]] && echo "Missing ~/.zen/secret.dunikey. EXIT" && exit 1 +G1PUB=$(cat ~/.zen/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +[[ $G1PUB == "" ]] && echo "ERROR G1PUB empty !! EXIT" && exit 1 +######################################################################## + +# GET NODE disk performance. TODO, publish and use as IPFS repartition +echo "DISK SIZE AVAILABLE & PERFORMANCE TESTING" +[[ -f ~/.ipfs/test.disk ]] && rm -f ~/.ipfs/test.disk +diskperf=$(dd if=/dev/zero of=~/.ipfs/test.disk bs=10M count=1 oflag=dsync 2>&1 | tail -n 1 | sed s/\,\ /\ -/g | cut -d '-' -f 4) +# echo $diskperf + +# IPFS LOCAL REPOSITORY for Node Identity +mkdir -p ~/.zen/ipfs/.$IPFSNODEID/G1SSB + +# find IPFSTRYME public address +echo "" > ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +for tryme in $(ipfs id | jq -r .Addresses[]); +do + isLAN=$(echo $tryme | cut -f3 -d '/' | grep -E "(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + [[ ! $isLAN ]] && echo "$tryme" >> ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +done +cat ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr + + +################################ +# ADD GCHANGE+ informations +GCHANGE="https://data.gchange.fr" + +# PREPARE title +# Made from Gchange+ profile tittle and city OR user@hostname +title=$(curl -s ${GCHANGE}/user/profile/${G1PUB} | jq -r '._source.title') +[[ -f ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && uidna=$(cat ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +# Put in .$IPFSNODEID INDEX: _g1.uidna & _g1.cesium_name (used by Minetest flavour and others) +[[ $uidna ]] && echo "$uidna" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.uidna +[[ $title ]] && echo "$title" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.gchange_name + +[[ $uidna ]] && [[ "$title" == "null" ]] && title="Station $uidna" +[[ "$title" == "null" ]] && title="Station $USER@$(cat /etc/hostname)" + +city=$(curl -s ${GCHANGE}/user/profile/${G1PUB} | jq -r '._source.city') +[[ "$city" != "null" ]] && title="$title in $city" + + +# ADD "cesium_geoPoint.lat" AND "cesium_geoPoint.lon" messages in SSB feed +# This way any SSB account is connected to its Cesium+ Geolocalisation. +geopointlat=$(curl -s ${GCHANGE}/user/profile/${G1PUB} | jq '._source.geoPoint.lat') +geopointlon=$(curl -s ${GCHANGE}/user/profile/${G1PUB} | jq '._source.geoPoint.lon') + +# REFRESH Cesium+ Avatar image +curl -s ${GCHANGE}/user/profile/${G1PUB} | jq -r '._source.avatar._content' | base64 -d > "/tmp/_gchange.avatar.png" + +# Get nodename +[[ -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename ]] && nodename=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename) +if [[ $nodename == "" ]]; then + nodename=$(cat /etc/hostname) + extension=$(echo $nodename | cut -d '.' -f 2) + if [[ $extension == $nodename ]]; then + nodename=$nodename.home + fi + echo "$nodename" > /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename +fi +######################################################################## +# DUNITER G1 Wallet balance +export LC_ALL=C.UTF-8 #attipix +export LANG=C.UTF-8 #attipix + +# COPY NODE G1SSB ID to IPFS +echo "$G1PUB" > ~/.zen/ipfs/.${IPFSNODEID}/G1SSB/_g1.pubkey + +# IPFS Node PUBLISH Adresses so Pub can become bootstrap for ${g1author} +ipfs id | jq -r .Addresses[] > ~/.zen/ipfs/.${IPFSNODEID}/Addresses + +# IPFS Node PUBLISH AgentVersion & repo.stat +ipfs id | jq -r .AgentVersion > ~/.zen/ipfs/.${IPFSNODEID}/AgentVersion +ipfs repo stat > ~/.zen/ipfs/.${IPFSNODEID}/repo.stat + +echo "$diskperf" > ~/.zen/ipfs/.${IPFSNODEID}/disk.perf +echo $(df ~/.ipfs/ | tail -n 1 | awk '{print $4}') > ~/.zen/ipfs/.${IPFSNODEID}/disk.bytes + + +## PUBLISH ~/.zen/ipfs on self key IPNS +IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --allow-offline --quieter /ipfs/$IWALLETS) + +### +# GET ALL MY GCHANGE FRIENDS AND SEND THEM my IPFS Address + +cd ~/.zen/astroport/zen/jaklis +# GET LIST of issuer(s) who likes me +for liking_me in $(./jaklis.py like | jq -r '.likes[].issuer'); +do + # CHECk if I am liking him either + friend_of_mine=$(./jaklis.py like -p $liking_me | jq -r '.yours'); + echo "Sending IPFSTRYME message to $liking_me" + [[ $friend_of_mine != null ]] && ./jaklis.py send -d $liking_me -t "ipfstryme" -f ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +done +cd - + +exit 0 diff --git a/zen/gchange_IPFS_swarm.sh b/zen/gchange_IPFS_swarm.sh new file mode 100755 index 0000000..4bb70e6 --- /dev/null +++ b/zen/gchange_IPFS_swarm.sh @@ -0,0 +1,113 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.12.03 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +# Connect to IPFS peers with bidirectionnal "like" (gchange friends) +# + +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# GET GCHANGE FRIENDS AND FIND THEIR IPFS ID TO "ipfs swarm connect" THEM +########################################################################' + +## TODO : Beware of liking_me FLOOD & Invite $liking_me people to my #Swarm0 + + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 +isIPFSRunning=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d ' ' -f 1) +[[ ! $isIPFSRunning ]] && echo "ERROR! ipfs daemon is not running. EXIT" && exit 1 +######################################################################## + +# Force Strict #swarm0 !!? +# ipfs bootstrap rm --all +echo "" > ~/.zen/A_allow_ip.txt + +echo "" > ~/.zen/A_liking_me_NO_ipfstryme.txt # contains "friends" with no "ipfstryme" incoming message +liking_me_count=0 +friend_of_mine_count=0 +######################################################################## +# Let's look for Friends and "IPFS swarm connect" with them +######################################################################## +cd ~/.zen/astroport/zen/jaklis +# 1. Get Gchange "liking_me" people list +for liking_me in $(./jaklis.py like | jq -r '.likes[].issuer'); +do + # Check if my like is reverse + friend_of_mine=$(./jaklis.py like -p $liking_me | jq -r '.yours'); + + [[ $friend_of_mine != null ]] && echo "Connect to my friend $liking_me" && friend_of_mine_count=$((friend_of_mine_count+1)) && ipfsadd=0 \ + && for peer in $(./jaklis.py read -n50 -j | jq -r --arg friendKEY "$liking_me" '.[] | select(.pubkey == $friendKEY)' | jq 'select(.title == "ipfstryme")' | jq -r '.content'); + # 2. Searching "ipfstryme" message from friend_of_mine + do + ip=$(echo "$peer" | awk -F '/' '{print $3}') && [[ $ip == "" ]] && continue || echo "Adding $ip to ~/.zen/A_allow_ip.txt" + # Fill a file with friend of mine ip + echo $ip >> ~/.zen/A_allow_ip.txt + # Get its ipfsnodeid + ipfsnodeid=$(echo "$peer" | awk -F '/' '{print $7}') + + # 3. ADD liking_me friend_of_mine to my swarm & bootstrap + ipfs swarm connect $peer; + ipfs bootstrap add $peer; + + ipfsadd=$((ipfsadd+1)) + done; + + #g1id=$(~/.zen/astroport/zen/tools/ipfs_to_g1.py "$ipfsnodeid") + #echo "G1 ID : $g1id" + + if [[ $friend_of_mine != null ]]; then + if [[ $ipfsadd == 0 ]]; then + # Friend of mine with no ipfstryme message (did not install astroport) + echo "No 'ipfstryme' message... from $liking_me" + echo "$liking_me" >> ~/.zen/A_liking_me_NO_ipfstryme.txt + + else + # REMOVING DUPLICATES OLD ipfstryme MESSAGES + nbmessage=0 + for messageid in $(./jaklis.py read -n50 -j | jq -r --arg friendKEY "$liking_me" '.[] | select(.pubkey == $friendKEY)' | jq 'select(.title == "ipfstryme")' | jq -r '.id') + do + nbmessage=$((nbmessage+1)) + [ $nbmessage -gt 1 ] && echo "Delete OLD 'ipfstryme' messages from $liking_me" && ./jaklis.py delete -i $messageid + done + fi + + fi + + # Automatic level_1 like from oasis.astroport.com (TODO add other default bootstrap) + # TODO get $g1pub from ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.pubkey ?? + # [[ $friend_of_mine == null && $G1PUB == "2jQUH4HfHxdTesjCjvMCx1VJgA5AnpuvrWRq1swfRdsS" ]] && ./jaklis.py like -p $g1pub -s 1 + echo "__________________________________________" + liking_me_count=$((liking_me_count+1)) + +done + + +echo "#################################################### +I have $friend_of_mine_count friends amoung $liking_me_count liking me people +__________________________________________ +My actual #SWARM0" +ipfs swarm peers +echo "__________________________________________" +echo +## Could send message to friends with 'ipfstryme' message +for line in $(cat ~/.zen/A_liking_me_NO_ipfstryme.txt | uniq); do + echo "Liking each other, but no ipfstryme received..." + echo "cd ~/.zen/astroport/zen/jaklis" + echo "./jaklis.py send -d $line -t 'Astroport' -m 'Rejoins mon #Swarm0 https://copylaradio.com'" +done + +cd - diff --git a/zen/gchange_MONITOR.sh b/zen/gchange_MONITOR.sh new file mode 100755 index 0000000..26cd8d8 --- /dev/null +++ b/zen/gchange_MONITOR.sh @@ -0,0 +1,118 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ +# Extract last ads +# Thank you @kimamila for cesium & gchange +# ES backend http://www.elasticsearchtutorial.com/spatial-search-tutorial.html +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized + +mkdir ~/.zen/cache/gchange -p + +ipfsnodeid=$(ipfs id -f='\n') +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) + +CESIUM="https://g1.data.le-sou.org" +GCHANGE="https://data.gchange.fr" # /user/profile/2L8vaYixCf97DMT8SistvQFeBj7vb6RQL7tvwyiv1XVH?&_source_exclude=avatar._content + +#curl -sk ${CESIUM}/user/profile/${g1pub} -o ~/.zen/cache/cesium_profile.json +LON=$(cat ~/.zen/cache/cesium_profile.json | jq '._source.geoPoint.lon') +LAT=$(cat ~/.zen/cache/cesium_profile.json | jq '._source.geoPoint.lat') + +curl -sk ${GCHANGE}/user/profile/${g1pub} -o ~/.zen/cache/GCHANGE_profile.json +LON=$(cat ~/.zen/cache/GCHANGE_profile.json | jq '._source.geoPoint.lon') +LAT=$(cat ~/.zen/cache/GCHANGE_profile.json | jq '._source.geoPoint.lat') + +RAD="$1" +[[ ! $RAD ]] && RAD="50km" + +if [[ "$LON" != "null" ]]; then +curl -sk -XPOST 'https://data.gchange.fr/market/record/_search?pretty&_source=title' -d ' + { + "size": 200, + "query": { + "bool": { + "filter": [{ + "geo_distance": { + "distance": "'$RAD'", + "geoPoint": { + "lat": '$LAT', + "lon": '$LON' + } + } + }] + } + } + }' > /tmp/gchange.json || exit 1 +else + echo "Aucune coordonnées geoPoint pour $g1pub" + sbotc publish '{"type":"post","text":"Ajouter sa géolocalisation dans Cesium+ permet de publier les annonces autour de chez soi..."}' + exit 1 +fi +TIMEBEFORE=$(date -u --date="-$DELAY" +"%s") +TIMESTAMP=$(date -u +"%s") +TOTAL=$(cat /tmp/gchange.json | jq .hits.total) +echo 'tail -f ~/.zen/cache/gchange.txt' +echo 'Annonces_Gchange' > ~/.zen/cache/gchange.txt +echo "Portefeuille_[June_:heart:](https://demo.cesium.app/#/app/wot/$g1pub/)" >> ~/.zen/cache/gchange.txt +echo "Carte_[$RAD](https://www.openstreetmap.org/#map=10/$LAT/$LON) " >> ~/.zen/cache/gchange.txt +chunk=0 +fullcount=0 + +DUNITERNODE=$($MY_PATH/tools/duniter_getnode.sh) +DUNITERURL="https://$DUNITERNODE" +LASTDU=$(curl -s ${DUNITERURL}/blockchain/with/ud | jq '.result.blocks[]' | tail -n 1); +[[ $LASTDU != "" ]] && LASTDU=$(curl -s ${DUNITERURL}/blockchain/block/${LASTDU} | jq '.dividend') +echo "DU = $LASTDU G1" + +for gID in $(cat /tmp/gchange.json | jq -r .hits.hits[]._id); do + + NEW="" + + [[ ! -f ~/.zen/cache/gchange/$gID.json ]] && + NEW="true" \ + && curl -s --create-dirs -o ~/.zen/cache/gchange/$gID.json -s https://data.gchange.fr/market/record/$gID?_source=category,title,description,issuer,time,creationTime,location,address,city,price,unit,currency,thumbnail._content_type,thumbnail._content,picturesCount,type,stock,fees,feesCurrency,geoPoint \ + && sleep $((1 + RANDOM % 3)) + + type=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.type) + stock=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.stock) + [[ $stock == 0 ]] && continue + + # [[ $type == "need" ]] && continue + creationTime=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.creationTime) + title=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.title) + + currency=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.currency) + price=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.price) + + categoryname=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.category.name) + + [[ $price == null ]] && price="0" + [[ $currency == "g1" ]] && love=$(bc -l <<< "scale=2; $price / $LASTDU * 100") || love="?.??" + love="$love_LOVE" + price=$(bc -l <<< "scale=2; $price / 100") + + fullcount=$((fullcount+1)) && echo "DEBUG : $fullcount - $type - $price $currency - $title " + [[ $price == "0" ]] && love="..." && price="A débattre " + + + [[ $type == "offer" ]] && LINE="___OFFRE___[$title](https://data.gchange.fr/market/record/$gID/_share)_$love" + [[ $type == "need" ]] && LINE="__DEMANDE__[$title](https://data.gchange.fr/market/record/$gID/_share)_$love" + + [[ $NEW == "true" ]] && echo "$LINE" >> ~/.zen/cache/gchange.txt && chunk=$((chunk+1)) && echo $chunk + +done +echo "$chunk_nouvelles_annonces_($TOTAL)" >> ~/.zen/cache/gchange.txt + +## TODO AUTOMATIC PUBLISHING \n and message size problem ?? +if [[ $(cat ~/.zen/cache/gchange.txt | wc -c) -lt 8000 ]]; then + export raw="$(cat ~/.zen/cache/gchange.txt)" + annonces=$(node -p "JSON.stringify(process.env.raw)") + sbotc publish '{"type":"post","text":'$annonces'}' +fi +# EXTRA COULD CREATE IT'S OWN MAP with https://github.com/zicmama/tile-stitch.git +# And magick to overlay... But best would be a local map proxy... diff --git a/zen/ipfs_EMPTY.sh b/zen/ipfs_EMPTY.sh new file mode 100755 index 0000000..a985504 --- /dev/null +++ b/zen/ipfs_EMPTY.sh @@ -0,0 +1,61 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +######################################################################## +# IPFS CLEANING (MANUAL OPERATION) +######################################################################## +ipfsnodeid=$(ipfs id -f='\n') +echo " + + _ _ ___ _ _ __ +/ | |_ /\ |\ | | |_)|_(_ +\_|_|_/--\| \| _|_| | __) + +LOVE BANK RAZ +You will have to do it yourself... +I cannot erase me. I will loose all my memory. + +Please be nice. Take care. + + _ _ _ _ +(_) | | ___ __ __ ___ | | | | +| | | | / _ \ \ \ / / / _ \ | | | | +| | | | | (_) | \ V / | __/ | |_| | +|_| |_| \___/ \_/ \___| \___/ + + + +### CLEAN IPFS REPO +################################################################## +ipfs pin ls -q --type recursive | xargs ipfs pin rm && ipfs repo gc + +echo 'Are you sure? LOVE economy is made of LOVE...' && exit 1 +### FORGET ZenTAG Wallets +################################################################## +rm -f ~/.ipfs/keystore/*.key +rm -Rf ~/.zen/tag/* +rm -Rf ~/.zen/ipfs/.$ipfsnodeid/TAG +rm -Rf ~/.zen/ipfs_swarm/.12D3KooW* +rm -Rf ~/.zen/ipfs_swarm/* + +### EMPTY miam STOMACK +################################################################## +rm -Rf ~/.zen/miam/* + +### FORGET G1 TX MEMORY +################################################################## +rm -Rf ~/.zen/cache/g1_TX_inputs/* + +################################################################## +# TODO: Howto Make ERASE reversable ?? +# BACKUP MYSELF in IPFS and Give pieces of my key to my friends... So if a part of you decide to keep me alive, I will still BE. +# TODO BACKUP BREAK MY KEY INTO ssss shards and put them in ~/.zen/ipfs/.QmFriends**/QmMeshard^* cyphered with its pubkey) +################################################################## +# WWWWOOOO, and what if Human life was the same... +# For you not to die, you need a minimum consciencious attention from part of the others that you are alive to continue living? +# Astonomical thought don't you thinks ;) +" + diff --git a/zen/ipfs_OPEN_ports.sh b/zen/ipfs_OPEN_ports.sh new file mode 100755 index 0000000..2ef72ed --- /dev/null +++ b/zen/ipfs_OPEN_ports.sh @@ -0,0 +1,37 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ +# Activate SUPPORT MODE: open ssh over IPFS +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" +WHOAMI=$(sbotc whoami | jq -r .id) || er+=" sbotc whoami problem" +[[ "$YOU" == "" || "$IPFSNODEID" == "" || "$WHOAMI" == "" ]] && echo "ERROR : $er " && exit 1 +######################################################################## +# TODO ESTABLISH A PORT FORWARD STRATEGY (depending on Node Flavour) + +# Arrange local port forwarded to swarm + +# GET _uidna (means g1sms/init.sh been run) +[[ -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && UIDNA=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +if [[ $(which gammu) ]]; then + # I am a g1sms NODE, pushing my web interface + ipfs p2p listen /x/g1sms /ip4/127.0.0.1/tcp/10099 +else + # Looking for g1sms NODE in my swarm + SMSNODE=$(ls /home/$YOU/.zen/ipfs_swarm/.12D3KooW*/G1SSB/_g1sms | shuf -n 1 | cut -d '/' -f 6 | cut -d '.' -f 2) + sleep $((1 + RANDOM % 10)) # Wait for DHT to propagate.... Then forward /x/g1sms + [[ $SMSNODE ]] && ipfs p2p forward /x/g1sms /ip4/127.0.0.1/tcp/10097 /p2p/$SMSNODE +fi + +# ipfs p2p close --all +# ipfs p2p listen /x/ssh-$UIDNA /ip4/127.0.0.1/tcp/22 +# ipfs p2p listen /x/http-$UIDNA /ip4/127.0.0.1/tcp/80 +# ipfs p2p listen /x/https-$UIDNA /ip4/127.0.0.1/tcp/443 +ipfs p2p ls diff --git a/zen/ipfs_SWARM_refresh.sh b/zen/ipfs_SWARM_refresh.sh new file mode 100755 index 0000000..ff63376 --- /dev/null +++ b/zen/ipfs_SWARM_refresh.sh @@ -0,0 +1,85 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.24 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +######################################################################## +# This script is controling IPFS swarm +# Sync friends peers ~/.zen/ipfs/ into ~/.zen/ipfs_swarm/.IPFSNODEID's/ directories +# add not friend_of_mine IPs to fail2ban +######################################################################## +# TODO remove because now it is in~/.bashrc +export YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) +export IPFSNODEID=$(ipfs id -f='\n') +######################################################################## +mkdir -p ~/.zen/ipfs_swarm +echo ' +___ _ _ __ __ _ _ _ _ _ _ __ + | |_)|_(_ (_\ //\ |_)|\/| |_)|_|_|_)|_(_ |_| +_|_| | __) __)\/\//--\| \| | | \|_| | \|___)| | + +' +echo "I am $IPFSNODEID" + +echo "REFRESHING /home/$YOU/.zen/ipfs_swarm/ from my SWARM peers" +rm -Rf /home/$YOU/.zen/ipfs_swarm/.12D3KooW* +rm -Rf /home/$YOU/.zen/ipfs_swarm/.Qm* +rm -Rf /home/$YOU/.zen/ipfs_swarm/* + +count=1 +for peer in $(ipfs swarm peers); +do + ipfsnodeid=$(echo "$peer" | awk -F '/' '{print $7}') + ip=$(echo "$peer" | awk -F '/' '{print $3}') + nowdate=$(date) + timestamp=$(date -u +%s%N | cut -b1-13) + + echo "$nowdate - $id - $ip" + foundIp=$(cat ~/.zen/A_allow_ip.txt | grep "$ip") + isLAN=$(echo $ip | cut -f3 -d '/' | grep -E "(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") + + if [[ ! $foundIp && ! $isLAN ]] ; then + echo "${ip} of peer ${id} is not in the authorized ip list." + echo "${peer} will be removed from the swarm" + + ipfs swarm disconnect $peer + ipfs bootstrap rm $peer + + echo "# FAIL2BAN # $USER must activate no password sudo (Rpi & Xbian OK)" + [[ $USER == "pi" || $USER == "xbian" ]] && echo "BAN $ip" \ + && sudo fail2ban-client add recidive \ + && sudo fail2ban-client set recidive banip $ip + + else + echo "${peer}" + echo "REFRESH /ipns/$ipfsnodeid INTO ~/.zen/ipfs_swarm/" + $MY_PATH/tools/timeout.sh -t 20 ipfs get --output=/home/$YOU/.zen/ipfs_swarm/ /ipns/$ipfsnodeid + + fi + +done + +echo "$IPFSNODEID PUBLISHING /home/$YOU/.zen/ipfs/ to SWARM" +[[ ! -d /home/$YOU/.zen/ipfs ]] && echo "ERROR : /home/$YOU/.zen/ipfs/ NOT FOUND" && exit 1 +IWALLETS=$(ipfs add -rHq /home/$YOU/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --quieter /ipfs/$IWALLETS) + +# Put my own data in /home/$YOU/.zen/ipfs_swarm/ +ipfs get --output=/home/$YOU/.zen/ipfs_swarm/ /ipns/$IPFSNODEID + +# NB: could start sync IPNS from "swarm peers" and check ~/.zen/ipfs_swarm/.$ipfsnodeid/G1SSB/_g1.pubkey +# if it is a gchange friend of mine... Then decide to bannish or keep. +# But IPNS sync with bad node could lead to a filesystem concistency corruption... +# Should be sync in a temp separate folder... So keeping IP control for now. + +exit 0 diff --git a/zen/ipfs_TASK_do.sh b/zen/ipfs_TASK_do.sh new file mode 100755 index 0000000..ed0ec35 --- /dev/null +++ b/zen/ipfs_TASK_do.sh @@ -0,0 +1,65 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# AJAX REQUEST TRACKER : ASTROPORT ON BOARDING +# GETTING : PHONE + PARRAIN + NAISSANCE +# +# DataFlow : index.html + ajaxform.js + zen_loveland_entrance.php +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized + +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +IPFSNODEID=$(ipfs id -f='\n') +NANODATE=$(date -u +%s%N) + +# SEARCH FOR NEW TASK OTHER NODE "todo.timestamp" +echo "REWRITING AND CHECK NEDDED" +exit 1 + +if [[ -d /home/$YOU/.zen/ipfs_swarm/.$IPFSNODEID/TASK ]]; then +############################################################################" +for task in $( ls /home/$YOU/.zen/ipfs_swarm/.$IPFSNODEID/TASK/todo.*); # "done.$NANODATE" FILES +do + FTASK=$( echo $task | cut -d '/' -f 8 ) # todo.$NANODATE + TNANO=$( echo $FTASK | cut -d '.' -f 2 ) # $NANODATE + echo "FOUND TASK FOR ME ($IPFSNODEID) : $FTASK " + # MAKE LOCAL .$IPFSNODEID directory + mkdir -p /home/$YOU/.zen/ipfs/.$IPFSNODEID/TASK/ + # TODO: CHECK BETTER NOT DONE YET and $NANODATE > $TNANO (=> detect NODES writing in the future!!) + if [[ ! -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/TASK/done.$TNANO ]]; then # NOT DONE YET: NEW TASK! + tdiff=$(bc -l <<< "$NANODATE - $TNANO") + if [[ $tdiff -gt 0 ]]; then + # todo.NANODATE is from past: OK. DO IT + echo "DOING it...." + chmod +x /home/$YOU/.zen/ipfs_swarm/.$IPFSNODEID/TASK/todo.$TNANO + cat /home/$YOU/.zen/ipfs_swarm/.$IPFSNODEID/TASK/todo.$TNANO + echo "WRITE todo RESULT in done.$TNANO" + else + # TODO: Bad NODE in the Future task !!! Make better BAD Node detection = Swarm Banish? + echo "KO.$tdiff" > /home/$YOU/.zen/ipfs/.$IPFSNODEID/TASK/done.$TNANO + echo " .$IPFSNODEID($FTASK) ERROR! DATE PROBLEM: $NANODATE < $TNANO :: KO" + + fi + fi +done + + echo "REMOVE OLD TASK MARKED AS DONE" + for scan in /home/$YOU/.zen/ipfs_swarm/.12D3KooW*/TASK/done.*; do + lscan=$(echo $scan | sed s/_swarm//g ) ## Remove _swarm + lid=$(echo $scan | cut -d '/' -f 6 | cut -d '.' -f 2 ) ## Get matching IPFSNODEID + lnano=$(echo $scan | cut -d '/' -f 8 | cut -d '.' -f 2 ) ## Get done timestamp + + if [[ "$lid" != "$IPFSNODEID" ]]; then + echo "CLEANING done OLD TASK ${lscan} SENT to $lid ($lnano.bin)" + rm -f ./wallets/.$lid/TASK/todo.$lnano + + fi + done +else + echo ".$IPFSNODEID :: NO TASK ! " +############################################################################" +fi diff --git a/zen/ipns_TAG_refresh.sh b/zen/ipns_TAG_refresh.sh new file mode 100755 index 0000000..93e6654 --- /dev/null +++ b/zen/ipns_TAG_refresh.sh @@ -0,0 +1,64 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.04.28 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# SYNC IPFS SWARM PEERS SHARED DATA .12D3KooW**** +########################################################################' + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 +######################################################################## +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +G1PUB=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +######################################################################## + +echo ' + + _ _ _ _ + / \ / \ / \ / \ +( I | P | N | S ) + \_/ \_/ \_/ \_/ + +ZENTAG REFRESH +' +echo "I am /ipns/$IPFSNODEID controling and refreshing my ZenTag" + +count=0 +[[ ! -d ~/.zen/tag/ ]] && exit 1 + +for id in ~/.zen/tag/*; # Alternative search +do + count=$((count+1)) + ipnskey=$(cat $id/_tag.uid) + zenvalue=$(cat $id/_tag.zen) + passengername=$(cat $id/_passenger.filename) + + echo "ZenTag $count : $ipnskey ($zenvalue Zen) $passengername" + # TODO: Add control to alert ZenTags strange behaviour + + # TODO :SECURITY BREACH: DO NOT PUBLISH _QRCODE.write.png !!! + + I=$(ipfs add -qr ${id} | tail -n 1) + # ZenTag IPNS name publish + Tkey=$(ipfs key list | grep -F ${ipnskey}.key) + J=$(ipfs name publish -k ${Tkey} --quieter /ipfs/${I}) + + echo "http://127.0.0.1/ipns/$J" + +done + diff --git a/zen/jaklis/.env b/zen/jaklis/.env new file mode 100644 index 0000000..93e5abf --- /dev/null +++ b/zen/jaklis/.env @@ -0,0 +1,4 @@ +DUNIKEY="/.zen/secret.dunikey" # Chemin du fichier de trousseau Ḡ1 de l'émetteur, au format PubSec +#POD="https://data.gchange.fr" # Noeud Gchange utilisé pour l'envoi du message +#POD="https://g1.data.duniter.fr" # Noeud Cecium+ utilisé pour l'envoi du message +POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours diff --git a/zen/jaklis/.env.template b/zen/jaklis/.env.template new file mode 100644 index 0000000..5063635 --- /dev/null +++ b/zen/jaklis/.env.template @@ -0,0 +1,4 @@ +DUNIKEY="" # Chemin de la clé privé Ḡ1 de l'émetteur, au format PubSec +#POD="https://g1.data.duniter.fr" # Adresse du pod Cesium ou Gchange à utiliser +POD="https://g1.data.le-sou.org" # Adresse du pod Cesium de secours +#POD="https://data.gchange.fr" # Adresse du pod ḠChange à utiliser \ No newline at end of file diff --git a/zen/jaklis/README.md b/zen/jaklis/README.md new file mode 100644 index 0000000..740f446 --- /dev/null +++ b/zen/jaklis/README.md @@ -0,0 +1,70 @@ +# Client CLI for Cesium+/Ḡchange pod +## Installation + +Linux: +``` +bash setup.sh +``` + +Autre: +``` +Débrouillez-vous. +``` + +## Utilisation + +Renseignez optionnellement le fichier **.env** (Généré lors de la première tentative d'execution, ou à copier depuis .env.template). + +``` +./jaklis.py -h +``` + +``` +usage: jaklis.py [-h] [-v] [-k KEY] [-n NODE] {read,send,delete,get,set,erase,like,unlike} ... + +positional arguments: + {read,send,delete,get,set,erase,like,unlike} + read Lecture des messages + send Envoi d'un message + delete Supression d'un message + get Voir un profile Cesium+ + set Configurer son profile Cesium+ + erase Effacer son profile Cesium+ + like Voir les likes d'un profile / Liker un profile (option -s NOTE) + unlike Supprimer un like + +optional arguments: + -h, --help show this help message and exit + -v, --version Affiche la version actuelle du programme + -k KEY, --key KEY Chemin vers mon trousseau de clé (PubSec) + -n NODE, --node NODE Adresse du noeud Cesium+ ou Gchange à utiliser +``` + +Utilisez `./jaklis CMD -h` où `CMD` est la commande souhaité pour obtenir l'aide détaillé de cette commande. + +### Exemples: + +Lire les 10 derniers messages de mon compte indiqué dans le fichier `.env` (par defaut 3 messages): +``` +./jaklis read -n10 +``` + +Envoyer un message à la clé publique `Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P` avec un fichier de trousseau particulier: +``` +./jaklis.py -k /home/saucisse/mon_fichier_de_trousseau.dunikey send -d Do99s6wQR2JLfhirPdpAERSjNbmjjECzGxHNJMiNKT3P -t "Objet du message" -m "Corps de mon message" +``` + +Noter 4 étoiles le profile `S9EJbjbaGPnp26VuV6fKjR7raE1YkNhUGDgoydHvAJ1` sur gchange: +``` +./jaklis.py -n https://data.gchange.fr like -p S9EJbjbaGPnp26VuV6fKjR7raE1YkNhUGDgoydHvAJ1 -s 4 +``` + +Paramétrer mon profile Cesium+: +``` +./jaklis.py set -n "Sylvain Durif" -v "Bugarach" -a "42 route de Vénus" -d "Christ cosmique" -pos 48.539927 2.6608169 -s https://www.creationmonetaire.info -A mon_avatar.png +``` + +Effacer mon profile Gchange: +``` +./jaklis.py -n https://data.gchange.fr erase +``` diff --git a/zen/jaklis/jaklis.py b/zen/jaklis/jaklis.py new file mode 100755 index 0000000..950e631 --- /dev/null +++ b/zen/jaklis/jaklis.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python3 + +import argparse, sys, os, random, string, getpass, json +from os.path import join, dirname +from shutil import copyfile +from dotenv import load_dotenv +from duniterpy.key import SigningKey +from lib.cesium import ReadFromCesium, SendToCesium, DeleteFromCesium, Profiles +from lib.likes import ReadLikes, SendLikes, UnLikes + +VERSION = "0.0.1" + +# Get variables environment +if not os.path.isfile('.env'): + copyfile(".env.template", ".env") +dotenv_path = join(dirname(__file__), '.env') +load_dotenv(dotenv_path) + +# Parse arguments +parser = argparse.ArgumentParser(description="Client CLI pour Cesium+ et Ḡchange") +parser.add_argument('-v', '--version', action='store_true', help="Affiche la version actuelle du programme") +parser.add_argument('-k', '--key', help="Chemin vers mon trousseau de clé (PubSec)") +parser.add_argument('-n', '--node', help="Adresse du noeud Cesium+ ou Gchange à utiliser") + +subparsers = parser.add_subparsers(title="Commandes de jaklis", dest="cmd") +read_cmd = subparsers.add_parser('read', help="Lecture des messages") +send_cmd = subparsers.add_parser('send', help="Envoi d'un message") +delete_cmd = subparsers.add_parser('delete', help="Supression d'un message") +getProfile_cmd = subparsers.add_parser('get', help="Voir un profile Cesium+") +setProfile_cmd = subparsers.add_parser('set', help="Configurer son profile Cesium+") +eraseProfile_cmd = subparsers.add_parser('erase', help="Effacer son profile Cesium+") +like_cmd = subparsers.add_parser('like', help="Voir les likes d'un profile / Liker un profile (option -s NOTE)") +unlike_cmd = subparsers.add_parser('unlike', help="Supprimer un like") + +# Messages management +read_cmd.add_argument('-n', '--number',type=int, default=3, help="Affiche les NUMBER derniers messages") +read_cmd.add_argument('-j', '--json', action='store_true', help="Sort au format JSON") +read_cmd.add_argument('-o', '--outbox', action='store_true', help="Lit les messages envoyés") + +send_cmd.add_argument('-d', '--destinataire', required=True, help="Destinataire du message") +send_cmd.add_argument('-t', '--titre', help="Titre du message à envoyer") +send_cmd.add_argument('-m', '--message', help="Message à envoyer") +send_cmd.add_argument('-f', '--fichier', help="Envoyer le message contenu dans le fichier 'FICHIER'") +send_cmd.add_argument('-o', '--outbox', action='store_true', help="Envoi le message sur la boite d'envoi") + +delete_cmd.add_argument('-i', '--id', action='append', nargs='+', required=True, help="ID(s) du/des message(s) à supprimer") +delete_cmd.add_argument('-o', '--outbox', action='store_true', help="Suppression d'un message envoyé") + +# Profiles management +setProfile_cmd.add_argument('-n', '--name', help="Nom du profile") +setProfile_cmd.add_argument('-d', '--description', help="Description du profile") +setProfile_cmd.add_argument('-v', '--ville', help="Ville du profile") +setProfile_cmd.add_argument('-a', '--adresse', help="Adresse du profile") +setProfile_cmd.add_argument('-pos', '--position', nargs=2, help="Points géographiques (lat + lon)") +setProfile_cmd.add_argument('-s', '--site', help="Site web du profile") +setProfile_cmd.add_argument('-A', '--avatar', help="Chemin vers mon avatar en PNG") + +getProfile_cmd.add_argument('-p', '--profile', help="Nom du profile") +getProfile_cmd.add_argument('-a', '--avatar', action='store_true', help="Récupérer également l'avatar au format raw base64") + +# Likes management +like_cmd.add_argument('-p', '--profile', help="Profile cible") +like_cmd.add_argument('-s', '--stars', type=int, help="Nombre d'étoile") +unlike_cmd.add_argument('-p', '--profile', help="Profile à déliker") + +args = parser.parse_args() +cmd = args.cmd + +if not cmd: + parser.print_help() + sys.exit(1) + +if args.version: + print(VERSION) + sys.exit(0) + +def createTmpDunikey(): + # Generate pseudo-random nonce + nonce=[] + for _ in range(32): + nonce.append(random.choice(string.ascii_letters + string.digits)) + nonce = ''.join(nonce) + keyPath = "/tmp/secret.dunikey-" + nonce + + key = SigningKey.from_credentials(getpass.getpass("Identifiant: "), getpass.getpass("Mot de passe: "), None) + key.save_pubsec_file(keyPath) + + return keyPath + +if args.node: + pod = args.node +else: + pod = os.getenv('POD') +if not pod: + pod="https://g1.data.le-sou.org" + +if args.key: + dunikey = args.key + keyPath = False +else: + dunikey = os.getenv('DUNIKEY') + if not dunikey: + keyPath = createTmpDunikey() + dunikey = keyPath + else: + keyPath = False +if not os.path.isfile(dunikey): + HOME = os.getenv("HOME") + dunikey = HOME + dunikey + if not os.path.isfile(dunikey): + sys.stderr.write('Le fichier de trousseau {0} est introuvable.\n'.format(dunikey)) + sys.exit(1) + + +# Build cesiumMessaging class +if cmd == "read": + messages = ReadFromCesium(dunikey, pod) + messages.read(args.number, args.outbox, args.json) +elif cmd == "send": + if args.fichier: + with open(args.fichier, 'r') as f: + msgT = f.read() + titre = msgT.splitlines(True)[0].replace('\n', '') + msg = ''.join(msgT.splitlines(True)[1:]) + if args.titre: + titre = args.titre + msg = msgT + elif args.titre and args.message: + titre = args.titre + msg = args.message + else: + titre = input("Indiquez le titre du message: ") + msg = input("Indiquez le contenu du message: ") + + messages = SendToCesium(dunikey, pod, args.destinataire, args.outbox) + messages.send(titre, msg) + +elif cmd == "delete": + messages = DeleteFromCesium(dunikey, pod, args.outbox) + messages.delete(args.id[0]) + +# Build cesium+ profiles class +elif cmd in ('set','get','erase'): + cesium = Profiles(dunikey, pod) + if cmd == "set": + cesium.set(args.name, args.description, args.ville, args.adresse, args.position, args.site, args.avatar) + elif cmd == "get": + cesium.get(args.profile, args.avatar) + elif cmd == "erase": + cesium.erase() + +# Build cesium+ likes class +elif cmd == "like": + if args.stars or args.stars == 0: + gchange = SendLikes(dunikey, pod) + gchange.like(args.stars, args.profile) + else: + gchange = ReadLikes(dunikey, pod) + gchange.readLikes(args.profile) +elif cmd == "unlike": + gchange = UnLikes(dunikey, pod) + gchange.unLike(args.profile) + + +if keyPath: + os.remove(keyPath) diff --git a/zen/jaklis/lib/__pycache__/cesium.cpython-36.pyc b/zen/jaklis/lib/__pycache__/cesium.cpython-36.pyc new file mode 100644 index 0000000..f7aebdb Binary files /dev/null and b/zen/jaklis/lib/__pycache__/cesium.cpython-36.pyc differ diff --git a/zen/jaklis/lib/__pycache__/likes.cpython-36.pyc b/zen/jaklis/lib/__pycache__/likes.cpython-36.pyc new file mode 100644 index 0000000..523efcd Binary files /dev/null and b/zen/jaklis/lib/__pycache__/likes.cpython-36.pyc differ diff --git a/zen/jaklis/lib/__pycache__/natools.cpython-36.pyc b/zen/jaklis/lib/__pycache__/natools.cpython-36.pyc new file mode 100644 index 0000000..86d4e07 Binary files /dev/null and b/zen/jaklis/lib/__pycache__/natools.cpython-36.pyc differ diff --git a/zen/jaklis/lib/cesium.py b/zen/jaklis/lib/cesium.py new file mode 100644 index 0000000..62d846b --- /dev/null +++ b/zen/jaklis/lib/cesium.py @@ -0,0 +1,557 @@ +#!/usr/bin/env python3 + +import os, sys, ast, requests, json, base58, base64, time, string, random, re +from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from time import sleep +from hashlib import sha256 +from datetime import datetime +from termcolor import colored + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +def pp_json(json_thing, sort=True, indents=4): + # Print beautifull JSON + if type(json_thing) is str: + print(json.dumps(json.loads(json_thing), sort_keys=sort, indent=indents)) + else: + print(json.dumps(json_thing, sort_keys=sort, indent=indents)) + return None + +class ReadFromCesium: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.recipient = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.recipient) or len(self.recipient) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, nbrMsg, outbox): + boxType = "issuer" if outbox else "recipient" + + data = {} + data['sort'] = { "time": "desc" } + data['from'] = 0 + data['size'] = nbrMsg + data['_source'] = ['issuer','recipient','title','content','time','nonce','read_signature'] + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = {} + data['query']['bool']['filter']['term'] = {} + data['query']['bool']['filter']['term'][boxType] = self.recipient + + document = json.dumps(data) + return document + + def sendDocument(self, nbrMsg, outbox): + boxType = "outbox" if outbox else "inbox" + + document = self.configDoc(nbrMsg, outbox) + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/message/{1}/_search'.format(self.pod, boxType), headers=headers, data=document) + if result.status_code == 200: + return result.json()["hits"] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text) + + # Parse JSON result and display messages + def readMessages(self, msgJSON, nbrMsg, outbox): + def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() + + # Get terminal size + rows = int(os.popen('stty size', 'r').read().split()[1]) + + totalMsg = msgJSON["total"] + if nbrMsg > totalMsg: + nbrMsg = totalMsg + + if totalMsg == 0: + print(colored("Aucun message à afficher.", 'yellow')) + return True + else: + infoTotal = " Nombre de messages: " + str(nbrMsg) + "/" + str(totalMsg) + " " + print(colored(infoTotal.center(rows, '#'), "yellow")) + for hits in msgJSON["hits"]: + self.idMsg = hits["_id"] + msgSrc = hits["_source"] + self.issuer = msgSrc["issuer"] + nonce = msgSrc["nonce"] + nonce = base58.b58decode(nonce) + self.dateS = msgSrc["time"] + date = datetime.fromtimestamp(self.dateS).strftime(", le %d/%m/%Y à %H:%M ") + if outbox: + startHeader = " À " + msgSrc["recipient"] + else: + startHeader = " De " + self.issuer + headerMsg = startHeader + date + "(ID: {})".format(self.idMsg) + " " + + print('-'.center(rows, '-')) + print(colored(headerMsg, "blue").center(rows+9, '-')) + print('-'.center(rows, '-')) + try: + self.title = decrypt(msgSrc["title"]) + self.content = decrypt(msgSrc["content"]) + except Exception as e: + sys.stderr.write(colored(str(e), 'red') + '\n') + pp_json(hits) + continue + print('\033[1m' + self.title + '\033[0m') + print(self.content) + + print(colored(infoTotal.center(rows, '#'), "yellow")) + + # Parse JSON result and display messages + def jsonMessages(self, msgJSON, nbrMsg, outbox): + def decrypt(msg): + msg64 = base64.b64decode(msg) + return box_decrypt(msg64, get_privkey(self.dunikey, "pubsec"), self.issuer, nonce).decode() + + totalMsg = msgJSON["total"] + if nbrMsg > totalMsg: + nbrMsg = totalMsg + + if totalMsg == 0: + print("Aucun message à afficher") + return True + else: + data = [] + # data.append({}) + # data[0]['total'] = totalMsg + for i, hits in enumerate(msgJSON["hits"]): + self.idMsg = hits["_id"] + msgSrc = hits["_source"] + self.issuer = msgSrc["issuer"] + nonce = msgSrc["nonce"] + nonce = base58.b58decode(nonce) + self.date = msgSrc["time"] + + if outbox: + pubkey = msgSrc["recipient"] + else: + pubkey = self.issuer + + try: + self.title = decrypt(msgSrc["title"]) + self.content = decrypt(msgSrc["content"]) + except Exception as e: + sys.stderr.write(colored(str(e), 'red') + '\n') + pp_json(hits) + continue + + data.append(i) + data[i] = {} + data[i]['id'] = self.idMsg + data[i]['date'] = self.date + data[i]['pubkey'] = pubkey + data[i]['title'] = self.title + data[i]['content'] = self.content + + data = json.dumps(data, indent=2) + return data + + def read(self, nbrMsg, outbox, isJSON): + jsonMsg = self.sendDocument(nbrMsg, outbox) + if isJSON: + jsonFormat = self.jsonMessages(jsonMsg, nbrMsg, outbox) + print(jsonFormat) + else: + self.readMessages(jsonMsg, nbrMsg, outbox) + + + + +#################### Sending class #################### + + + + +class SendToCesium: + def __init__(self, dunikey, pod, recipient, outbox): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + self.recipient = recipient + self.outbox = outbox + + # Generate pseudo-random nonce + nonce=[] + for _ in range(32): + nonce.append(random.choice(string.ascii_letters + string.digits)) + self.nonce = base64.b64decode(''.join(nonce)) + + if not re.match(PUBKEY_REGEX, recipient) or len(recipient) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + + def encryptMsg(self, msg): + return fmt["64"](box_encrypt(msg.encode(), get_privkey(self.dunikey, "pubsec"), self.recipient, self.nonce)).decode() + + def configDoc(self, title, msg): + b58nonce = base58.b58encode(self.nonce).decode() + + # Get current timestamp + timeSent = int(time.time()) + + # Generate custom JSON + data = {} + data['issuer'] = self.issuer + data['recipient'] = self.recipient + data['title'] = title + data['content'] = msg + data['time'] = timeSent + data['nonce'] = b58nonce + data['version'] = 2 + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + finalDoc = '{' + '"hash":"{0}","signature":"{1}",'.format(hashDoc, signature) + document[1:] + + return finalDoc + + + def sendDocument(self, document): + boxType = "outbox" if self.outbox else "inbox" + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/message/{1}?pubkey={2}'.format(self.pod, boxType, self.recipient), headers=headers, data=document) + except Exception as e: + sys.stderr.write("Impossible d'envoyer le message:\n" + str(e)) + sys.exit(1) + else: + if result.status_code == 200: + print(colored("Message envoyé avec succès !", "green")) + print("ID: " + result.text) + return result + else: + sys.stderr.write("Erreur inconnue:" + '\n') + print(str(pp_json(result.text)) + '\n') + + def send(self, title, msg): + finalDoc = self.configDoc(self.encryptMsg(title), self.encryptMsg(msg)) # Configure JSON document to send + self.sendDocument(finalDoc) # Send final signed document + + + + +#################### Deleting class #################### + + + + +class DeleteFromCesium: + def __init__(self, dunikey, pod, outbox): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + self.outbox = outbox + + + def configDoc(self, idMsg): + # Get current timestamp + timeSent = int(time.time()) + + boxType = "outbox" if self.outbox else "inbox" + + # Generate document to customize + data = {} + data['version'] = 2 + data['index'] = "message" + data['type'] = boxType + data['id'] = idMsg + data['issuer'] = self.issuer + data['time'] = timeSent + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, idMsg): + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get result + try: + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + if result.status_code == 404: + raise ValueError("Message introuvable") + elif result.status_code == 403: + raise ValueError("Vous n'êtes pas l'auteur de ce message.") + except Exception as e: + sys.stderr.write(colored("Impossible de supprimer le message {0}:\n".format(idMsg), 'red') + str(e) + "\n") + return False + else: + if result.status_code == 200: + print(colored("Message {0} supprimé avec succès !".format(idMsg), "green")) + return result + else: + sys.stderr.write("Erreur inconnue.") + + def delete(self, idsMsgList): + for idMsg in idsMsgList: + finalDoc = self.configDoc(idMsg) + self.sendDocument(finalDoc, idMsg) + + + + + +#################### Profile class #################### + + + + + +class Profiles: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.pubkey = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.pubkey) or len(self.pubkey) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document SET to send + def configDocSet(self, name, description, city, address, pos, socials, avatar): + timeSent = int(time.time()) + + data = {} + if name: data['title'] = name + if description: data['description'] = description + if address: data['address'] = address + if city: data['city'] = city + if pos: + geoPoint = {} + geoPoint['lat'] = pos[0] + geoPoint['lon'] = pos[1] + data['geoPoint'] = geoPoint + if socials: + data['socials'] = [] + data['socials'].append({}) + data['socials'][0]['type'] = "web" + data['socials'][0]['url'] = socials + if avatar: + avatar = open(avatar, 'rb').read() + avatar = base64.b64encode(avatar).decode() + data['avatar'] = {} + data['avatar']['_content'] = avatar + data['avatar']['_content_type'] = "image/png" + data['time'] = timeSent + data['issuer'] = self.pubkey + data['version'] = 2 + data['tags'] = [] + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + # Configure JSON document GET to send + def configDocGet(self, profile, scope='title', getAvatar=None): + + if getAvatar: + avatar = "avatar" + else: + avatar = "avatar._content_type" + + data = { + "query": { + "bool": { + "should":[ + { + "match":{ + scope:{ + "query": profile,"boost":2 + } + } + },{ + "prefix": {scope: profile} + } + ] + } + },"highlight": { + "fields": { + "title":{}, + "tags":{} + } + },"from":0, + "size":100, + "_source":["title", avatar,"description","city","address","socials.url","creationTime","membersCount","type"], + "indices_boost":{"user":100,"page":1,"group":0.01 + } + } + + document = json.dumps(data) + + return document + + # Configure JSON document SET to send + def configDocErase(self): + timeSent = int(time.time()) + + data = {} + data['time'] = timeSent + data['id'] = self.pubkey + data['issuer'] = self.pubkey + data['version'] = 2 + data['index'] = "user" + data['type'] = "profile" + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, type): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + if type == 'set': + reqQuery = '{0}/user/profile?pubkey={1}/_update?pubkey={1}'.format(self.pod, self.pubkey) + elif type == 'get': + reqQuery = '{0}/user,page,group/profile,record/_search'.format(self.pod) + elif type == 'erase': + reqQuery = '{0}/history/delete'.format(self.pod) + + result = requests.post(reqQuery, headers=headers, data=document) + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document...\n" + result.text + '\n') + + def parseJSON(self, doc): + doc = json.loads(doc)['hits']['hits'] + if doc: + pubkey = { "pubkey": doc[0]['_id'] } + rest = doc[0]['_source'] + final = {**pubkey, **rest} + else: + final = 'Profile vide' + + return json.dumps(final, indent=2) + + + def set(self, name=None, description=None, ville=None, adresse=None, position=None, site=None, avatar=None): + document = self.configDocSet(name, description, ville, adresse, position, site, avatar) + result = self.sendDocument(document,'set') + + print(result) + return result + + def get(self, profile=None, avatar=None): + if not profile: + profile = self.pubkey + if not re.match(PUBKEY_REGEX, profile) or len(profile) > 45: + scope = 'title' + else: + scope = '_id' + + document = self.configDocGet(profile, scope, avatar) + resultJSON = self.sendDocument(document, 'get') + result = self.parseJSON(resultJSON) + + print(result) + return result + + def erase(self): + document = self.configDocErase() + result = self.sendDocument(document,'erase') + + print(result) + return result \ No newline at end of file diff --git a/zen/jaklis/lib/likes.py b/zen/jaklis/lib/likes.py new file mode 100644 index 0000000..ce4b1f6 --- /dev/null +++ b/zen/jaklis/lib/likes.py @@ -0,0 +1,331 @@ +#!/usr/bin/env python3 + +import os, sys, ast, requests, json, base58, base64, time, string, random, re +from lib.natools import fmt, sign, get_privkey, box_decrypt, box_encrypt +from time import sleep +from hashlib import sha256 +from datetime import datetime +from termcolor import colored + +PUBKEY_REGEX = "(?![OIl])[1-9A-Za-z]{42,45}" + +class ReadLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, profile): + if not profile: profile = self.issuer + # elif len(profile) < 42: + # print(len(profile)) + # gProfile = requests.get('{0}/user/profile/{1}'.format(self.pod, issuer)) + # gProfile = json.loads(gProfile.text)['_source'] + # pseudo = gProfile['title'] + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'index': 'user'}}, + {'term': {'type': 'profile'}}, + {'term': {'id': profile}}, + {'term': {'kind': 'STAR'}} + ] + # data['query']['bool']['should'] = {'term':{'issuer': self.issuer}} + data['size'] = 5000 + data['_source'] = ['issuer','level'] + data['aggs'] = { + 'level_sum': { + 'sum': { + 'field': 'level' + } + } + } + + return json.dumps(data) + + def sendDocument(self, document): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/like/record/_search'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + # print(result.text) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') + + def parseResult(self, result): + result = json.loads(result) + totalLikes = result['hits']['total'] + totalValue = result['aggregations']['level_sum']['value'] + if totalLikes: + score = totalValue/totalLikes + else: + score = 0 + raw = result['hits']['hits'] + finalPrint = {} + finalPrint['likes'] = [] + for i in raw: + issuer = i['_source']['issuer'] + # print(issuer) + gProfile = self.getProfile(issuer) + try: + pseudo = gProfile['title'] + except: + pseudo = '' + try: + payTo = gProfile['pubkey'] + except: + payTo = '' + id = i['_id'] + level = i['_source']['level'] + if issuer == self.issuer: + finalPrint['yours'] = { 'id' : id, 'pseudo' : pseudo, 'payTo' : payTo, 'level' : level } + else: + finalPrint['likes'].append({ 'issuer' : issuer, 'pseudo' : pseudo, 'payTo' : payTo, 'level' : level }) + finalPrint['score'] = score + + return json.dumps(finalPrint) + + def getProfile(self, profile): + headers = { + 'Content-type': 'application/json', + } + + data = {} + data['query'] = {} + data['query']['bool'] = {} + data['query']['bool']['filter'] = [ + {'term': {'_index': 'user'}}, + {'term': {'_type': 'profile'}}, + {'term': {'_id': profile}} + ] + data['_source'] = ['title','pubkey'] + + data = json.dumps(data) + + result = requests.post('{0}/user/profile/_search'.format(self.pod), headers=headers, data=data) + result = json.loads(result.text)['hits']['hits'][0]['_source'] + + return result + + def readLikes(self, profile=False): + document = self.configDoc(profile) + result = self.sendDocument(document) + result = self.parseResult(result) + + print(result) + return result + + + + +#################### Like class #################### + + + + +class SendLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Configure JSON document to send + def configDoc(self, profile, likes): + if not profile: profile = self.issuer + if likes not in range(0, 6): + sys.stderr.write(colored('Votre like doit être compris entre 0 et 5.\n', 'red')) + return False + + + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "user" + data['type'] = "profile" + data['id'] = profile + data['kind'] = "STAR" + data['level'] = likes + data['time'] = timeSent + data['issuer'] = self.issuer + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, pubkey): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/user/profile/:id/_like'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + print(colored("Profile liké avec succès !", 'green')) + return result.text + elif result.status_code == 400: + resultJson = json.loads(result.text) + if 'DuplicatedDocumentException' in resultJson['error']: + rmLike = UnLikes(self.dunikey, self.pod) + rmLike.unLike(pubkey, True) + sleep(0.5) + self.sendDocument(document, pubkey) + return resultJson['error'] + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + else: + resultJson = json.loads(result.text) + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + resultJson['error'] + '\n') + + + + + def like(self, stars, profile=False): + document = self.configDoc(profile, stars) + if document: + self.sendDocument(document, profile) + + + + +#################### Unlike class #################### + + + + +class UnLikes: + def __init__(self, dunikey, pod): + # Get my pubkey from my private key + try: + self.dunikey = dunikey + if not dunikey: + raise ValueError("Dunikey is empty") + except: + sys.stderr.write("Please fill the path to your private key (PubSec)\n") + sys.exit(1) + + self.issuer = get_privkey(dunikey, "pubsec").pubkey + self.pod = pod + + if not re.match(PUBKEY_REGEX, self.issuer) or len(self.issuer) > 45: + sys.stderr.write("La clé publique n'est pas au bon format.\n") + sys.exit(1) + + # Check if you liked this profile + def checkLike(self, pubkey): + + readProfileLikes = ReadLikes(self.dunikey, self.pod) + document = readProfileLikes.configDoc(pubkey) + result = readProfileLikes.sendDocument(document) + result = readProfileLikes.parseResult(result) + result = json.loads(result) + + if 'yours' in result: + myLike = result['yours']['id'] + return myLike + else: + sys.stderr.write("Vous n'avez pas liké ce profile\n") + return False + + # Configure JSON document to send + def configDoc(self, idLike): + timeSent = int(time.time()) + + data = {} + data['version'] = 2 + data['index'] = "like" + data['type'] = "record" + data['id'] = idLike + data['issuer'] = self.issuer + data['time'] = timeSent + + document = json.dumps(data) + + # Generate hash of document + hashDoc = sha256(document.encode()).hexdigest().upper() + + # Generate signature of document + signature = fmt["64"](sign(hashDoc.encode(), get_privkey(self.dunikey, "pubsec"))[:-len(hashDoc.encode())]).decode() + + # Build final document + data = {} + data['hash'] = hashDoc + data['signature'] = signature + signJSON = json.dumps(data) + finalJSON = {**json.loads(signJSON), **json.loads(document)} + finalDoc = json.dumps(finalJSON) + + return finalDoc + + def sendDocument(self, document, silent): + + headers = { + 'Content-type': 'application/json', + } + + # Send JSON document and get JSON result + result = requests.post('{0}/history/delete'.format(self.pod), headers=headers, data=document) + + if result.status_code == 200: + if not silent: + print(colored("Like supprimé avec succès !", 'green')) + return result.text + else: + sys.stderr.write("Echec de l'envoi du document de lecture des messages...\n" + result.text + '\n') + + + def unLike(self, pubkey, silent=False): + idLike = self.checkLike(pubkey) + if idLike: + document = self.configDoc(idLike) + self.sendDocument(document, silent) + diff --git a/zen/jaklis/lib/natools.py b/zen/jaklis/lib/natools.py new file mode 100755 index 0000000..18f06d1 --- /dev/null +++ b/zen/jaklis/lib/natools.py @@ -0,0 +1,297 @@ +#!/usr/bin/env python3 + +""" + CopyLeft 2020 Pascal Engélibert + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +""" + +__version__ = "1.3.1" + +import os, sys, duniterpy.key, libnacl, base58, base64, getpass + +def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str: + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +def read_data(data_path, b=True): + if data_path == "-": + if b: + return sys.stdin.buffer.read() + else: + return sys.stdin.read() + else: + return open(os.path.expanduser(data_path), "rb" if b else "r").read() + +def write_data(data, result_path): + if result_path == "-": + os.fdopen(sys.stdout.fileno(), 'wb').write(data) + else: + open(os.path.expanduser(result_path), "wb").write(data) + +def encrypt(data, pubkey): + return duniterpy.key.PublicKey(pubkey).encrypt_seal(data) + +def decrypt(data, privkey): + return privkey.decrypt_seal(data) + +def box_encrypt(data, privkey, pubkey, nonce=None, attach_nonce=False): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + data = box.encrypt(data, nonce) if nonce else box.encrypt(data) + return data if attach_nonce else data[24:] + +def box_decrypt(data, privkey, pubkey, nonce=None): + signer = libnacl.sign.Signer(privkey.seed) + sk = libnacl.public.SecretKey(libnacl.crypto_sign_ed25519_sk_to_curve25519(signer.sk)) + verifier = libnacl.sign.Verifier(base58.b58decode(pubkey).hex()) + pk = libnacl.public.PublicKey(libnacl.crypto_sign_ed25519_pk_to_curve25519(verifier.vk)) + box = libnacl.public.Box(sk.sk, pk.pk) + return box.decrypt(data, nonce) if nonce else box.decrypt(data) + +def sign(data, privkey): + return privkey.sign(data) + +def verify(data, pubkey): + try: + ret = libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data) + sys.stderr.write("Signature OK!\n") + return ret + except ValueError: + sys.stderr.write("Bad signature!\n") + exit(1) + +def get_privkey(privkey_path, privkey_format): + if privkey_format == "pubsec": + if privkey_path == "*": + privkey_path = "privkey.pubsec" + return duniterpy.key.SigningKey.from_pubsec_file(privkey_path) + + elif privkey_format == "cred": + if privkey_path == "*": + privkey_path = "-" + if privkey_path == "-": + return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Password: "), getpass.getpass("Salt: ")) + else: + return duniterpy.key.SigningKey.from_credentials_file(privkey_path) + + elif privkey_format == "seedh": + if privkey_path == "*": + privkey_path = "authfile.seedhex" + return duniterpy.key.SigningKey.from_seedhex(read_data(privkey_path, False)) + + elif privkey_format == "wif": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_file(privkey_path) + + elif privkey_format == "wifh": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_hex(privkey_path) + + elif privkey_format == "ssb": + if privkey_path == "*": + privkey_path = "secret" + return duniterpy.key.SigningKey.from_ssb_file(privkey_path) + + elif privkey_format == "key": + if privkey_path == "*": + privkey_path = "authfile.key" + return duniterpy.key.SigningKey.from_private_key(privkey_path) + + print("Error: unknown privkey format") + +def fill_pubkey(pubkey, length=32): + while pubkey[0] == 0: + pubkey = pubkey[1:] + return b"\x00"*(length-len(pubkey)) + pubkey + +def pubkey_checksum(pubkey, length=32, clength=3): + return base58.b58encode(libnacl.crypto_hash_sha256(libnacl.crypto_hash_sha256(fill_pubkey(base58.b58decode(pubkey), length)))).decode()[:clength] + +# returns (pubkey:bytes|None, deprecated_length:bool) +def check_pubkey(pubkey): + if ":" in pubkey: + parts = pubkey.split(":") + if len(parts[1]) < 3 or len(parts[1]) > 32: + return (None, False) + for i in range(32, 0, -1): + if pubkey_checksum(parts[0], i, len(parts[1])) == parts[1]: + return (parts[0], i < 32) + return (None, False) + return (pubkey, False) + +fmt = { + "raw": lambda data: data, + "16": lambda data: data.hex().encode(), + "32": lambda data: base64.b32encode(data), + "58": lambda data: base58.b58encode(data), + "64": lambda data: base64.b64encode(data), + "64u": lambda data: base64.urlsafe_b64encode(data), + "85": lambda data: base64.b85encode(data), +} + +defmt = { + "raw": lambda data: data, + "16": lambda data: bytes.fromhex(data), + "32": lambda data: base64.b32decode(data), + "58": lambda data: base58.b58decode(data), + "64": lambda data: base64.b64decode(data), + "85": lambda data: base64.b85decode(data), +} + +def show_help(): + print("""Usage: +python3 natools.py [options] + +Commands: + encrypt Encrypt data + decrypt Decrypt data + box-encrypt Encrypt data (NaCl box) + box-decrypt Decrypt data (NaCl box) + sign Sign data + verify Verify data + pubkey Display pubkey + pk Display b58 pubkey shorthand + +Options: + -c Display pubkey checksum + -f Private key format (default: cred) + key cred pubsec seedh ssb wif wifh + -i Input file path (default: -) + -I Input format: raw 16 32 58 64 85 (default: raw) + -k Privkey file path (* for auto) (default: *) + -n Nonce (b64, 24 bytes) (for NaCl box) + -N Attach nonce to output (for NaCl box encryption) + --noinc Do not include msg after signature + -o Output file path (default: -) + -O Output format: raw 16 32 58 64 64u 85 (default: raw) + -p Pubkey (base58) + + --help Show help + --version Show version + --debug Debug mode (display full errors) + +Note: "-" means stdin or stdout. +""") + +if __name__ == "__main__": + + if "--help" in sys.argv: + show_help() + exit() + + if "--version" in sys.argv: + print(__version__) + exit() + + privkey_format = getargv("-f", "cred") + data_path = getargv("-i", "-") + privkey_path = getargv("-k", "*") + pubkey = getargv("-p") + result_path = getargv("-o", "-") + output_format = getargv("-O", "raw") + input_format = getargv("-I", "raw") + + if pubkey: + pubkey, len_deprecated = check_pubkey(pubkey) + if not pubkey: + print("Invalid pubkey checksum! Please check spelling.") + exit(1) + if len(base58.b58decode(pubkey)) > 32: + print("Invalid pubkey: too long!") + exit(1) + if len_deprecated: + print("Warning: valid pubkey checksum, but deprecated format (truncating zeros)") + + try: + if sys.argv[1] == "encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](encrypt(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "decrypt": + write_data(fmt[output_format](decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format))), result_path) + + elif sys.argv[1] == "box-encrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + attach_nonce = "-N" in sys.argv + write_data(fmt[output_format](box_encrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce, attach_nonce)), result_path) + + elif sys.argv[1] == "box-decrypt": + if not pubkey: + print("Please provide pubkey!") + exit(1) + nonce = getargv("-n", None) + if nonce: + nonce = base64.b64decode(nonce) + write_data(fmt[output_format](box_decrypt(defmt[input_format](read_data(data_path)), get_privkey(privkey_path, privkey_format), pubkey, nonce)), result_path) + + elif sys.argv[1] == "sign": + data = defmt[input_format](read_data(data_path)) + signed = sign(data, get_privkey(privkey_path, privkey_format)) + + if "--noinc" in sys.argv: + signed = signed[:len(signed)-len(data)] + + write_data(fmt[output_format](signed), result_path) + + elif sys.argv[1] == "verify": + if not pubkey: + print("Please provide pubkey!") + exit(1) + write_data(fmt[output_format](verify(defmt[input_format](read_data(data_path)), pubkey)), result_path) + + elif sys.argv[1] == "pubkey": + if pubkey: + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + else: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv and output_format == "58": + write_data("{}:{}".format(pubkey, pubkey_checksum(pubkey)).encode(), result_path) + else: + write_data(fmt[output_format](base58.b58decode(pubkey)), result_path) + + elif sys.argv[1] == "pk": + if not pubkey: + pubkey = get_privkey(privkey_path, privkey_format).pubkey + if "-c" in sys.argv: + print("{}:{}".format(pubkey, pubkey_checksum(pubkey))) + else: + print(pubkey) + + else: + show_help() + + except Exception as e: + if "--debug" in sys.argv: + 0/0 # DEBUG MODE (raise error when handling error to display backtrace) + sys.stderr.write("Error: {}\n".format(e)) + show_help() + exit(1) diff --git a/zen/jaklis/requirements.txt b/zen/jaklis/requirements.txt new file mode 100644 index 0000000..bf25335 --- /dev/null +++ b/zen/jaklis/requirements.txt @@ -0,0 +1,6 @@ +wheel +base58 +pybase64 +duniterpy +termcolor +python-dotenv diff --git a/zen/jaklis/setup.sh b/zen/jaklis/setup.sh new file mode 100755 index 0000000..222e4ba --- /dev/null +++ b/zen/jaklis/setup.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +for i in gcc python3-pip python3-setuptools libpq-dev python3-dev python3-wheel; do + if [ $(dpkg-query -W -f='${Status}' $i 2>/dev/null | grep -c "ok installed") -eq 0 ]; then + [[ ! $j ]] && sudo apt update + sudo apt install -y $i + j=1 + fi +done + +pip3 install -r requirements.txt +chmod u+x jaklis.py diff --git a/zen/miam_miam.sh b/zen/miam_miam.sh new file mode 100755 index 0000000..e9fc9e0 --- /dev/null +++ b/zen/miam_miam.sh @@ -0,0 +1,127 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.04.16 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# \\/// +# qo-op +############# '$ME' HELLO!! I am getting files from ~/.zen/miam +######################################################################## +# ex: '$MY_PATH'/'$ME' +# MOVE ~/.zen/miam/* IN ZenTag AS PASSENGER +# Then find SSB same timestamp message and make a reply ;) +######################################################################## +# [ASTROPORT](https://astroport.com) +######################################################################## +echo ' + ___ +|\/| | /\ |\/| |_ ._ _ ._ +| | _|_ /--\ | | | | |_| | | (_| | \/ + _| / + +' +tstamp="$1" + +# for tstamp in $(ls ~/.zen/miam/); do + [[ $tstamp == "" ]] && echo "MISSING tstamp $tstamp" && exit 1 + [ ! -d ~/.zen/miam/$tstamp ] && echo "NOT FOUND ~/.zen/miam/$tstamp" && exit 1 + [ -f ~/.zen/miam/$tstamp/msg_key ] && msg_key=$(cat ~/.zen/miam/$tstamp/msg_key) || echo "HEY I cannot find source SSB message in $tstamp" || exit 1 + + echo "$msg_key" + msg=$(sbotc get '{"id":"'"$msg_key"'"}') + + [[ $msg == "" ]] && echo "No SSB message for $tstamp" && continue + msg_root=$(printf %s "$msg" | jq -r .value.content.root) + msg_branch=$(printf %s "$msg" | jq -r .value.content.branch) + # TREATING miam $tstamp FILES + echo "##############################################################" + for file in ~/.zen/miam/$tstamp/*; do + # file --mime-type "$file" + filename=$(basename -- "$file") + extension="${filename##*.}" + filena="${filename%.*}" + + case "$extension" in + + json) + JSON="$file" + echo "# METADATA FILE" + echo "$filename" + extractor=$(cat "$file" | jq -r '.extractor') + if [[ $extractor == "youtube" ]]; then + id=$(cat "$file" | jq -r '.id') + fulltitle=$(cat "$file" | jq -r '.fulltitle') + description=$(cat "$file" | jq -r '.description') + artist=$(cat "$file" | jq -r '.artist') + album=$(cat "$file" | jq -r '.album') + duration=$(cat "$file" | jq -r '.duration') + upload_date=$(cat "$file" | jq -r '.upload_date') + uploader_id=$(cat "$file" | jq -r '.uploader_id') + echo "YOUTUBE: $id : $fulltitle ($duration s) by $uploader_id " + else + echo "ERROR Unknown METADATA TYPE, please add some code here..." && JSON="" + fi + continue + ;; + + jpg) + JPG="$file" + echo "# THUMBNAIL FILE" + echo "$filename" + convert "$file" -strip -resize 640 -format jpg ~/.zen/miam/$tstamp/ssb_thumb.jpg + + continue + ;; + + mp4) + MP4="$file" + echo "# VIDEO FILE" + echo "$filename" + + continue + ;; + + + mp3) + MP3="$file" + echo "# AUDIO FILE" + echo "$filename" + continue + ;; + + *) + echo "TYPE is UNKNOWN for $file" && UNKOWN="$file" + continue + ;; + + esac + done + # File analyse finished... + # WHAT DO WE HAVE THERE? + [[ "$JSON" == "" ]] && echo "NO METADATA !!!!" \ + && echo "BAD PASSENGER ERASING ~/.zen/miam/$tstamp" && rm -Rf ~/.zen/miam/$tstamp \ + && exit 1 + + [[ -f $MP3 ]] && $MY_PATH/zen_MAKE.sh "1000" "$tstamp" "$MP3" "$JSON" "10" "10" + [[ -f $MP4 ]] && $MY_PATH/zen_MAKE.sh "1000" "$tstamp" "$MP4" "$JSON" "10" "10" + +# [[ -f $MP3 ]] && mv "$MP3" "~/.zen/audio/$fulltitle.mp3" # && lltag -a "$artist" -A "$album" -t "$fulltitle" "~/.zen/audio/$fulltitle.mp3" +# [[ -f $MP4 ]] && mv "$MP4" "~/.zen/video/$fulltitle.mp4" + + # CLEAN A LITTLE + UNKOWN="" + MP3="" + MP4="" + JPG="" + JSON="" + [[ -d ~/.zen/miam/$tstamp && "$tstamp" != "" ]] && rm -Rf ~/.zen/miam/$tstamp && echo "# DELETE miam, it has been EATEN" + + exit 0 + +#done diff --git a/zen/no_SPAM.sh b/zen/no_SPAM.sh new file mode 100755 index 0000000..2d62c9d --- /dev/null +++ b/zen/no_SPAM.sh @@ -0,0 +1,44 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.18 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +# \\/// +# qo-op +############# +########################## +####################################### +#################################################### +######################################################################## + +######################################################################## +# check /tmp/cmd_received_$SOURCEH and /tmp/cmd_SPAM_$SOURCEH within 1mn + +# Remove older than a minute received COMMAND +find /tmp -cmin +1 -type f -name "cmd_received_*" -exec rm -f '{}' \; 2>/dev/null +# Filter Acknowlegement +if [[ "$CMD" != "ACK" && "$CMD" != "DELIVERED" && "$CMD" != "PENDING" ]]; then + # Still less than a minute with same $SOURCEH + if [[ -f "/tmp/cmd_received_$SOURCEH" ]]; then + # Create SPAM file => Stop answering + if [[ ! -f "/tmp/cmd_SPAM_$SOURCEH" ]]; then + echo $(date) > "/tmp/cmd_SPAM_$SOURCEH" + fi + return 1 + fi + echo "$COMMAND" > "/tmp/cmd_received_$SOURCEH" + # Remove SPAM flag older than one day + find /tmp -ctime +1 -type f -name "cmd_SPAM_*" -exec rm -f '{}' \; 2>/dev/null +else + # THIS IS AN AKNOWLEGEMENT + return 1 +fi +# Remove SPAM flag older than one day +find /tmp -ctime +1 -type f -name "cmd_SPAM_*" -exec rm -f '{}' \; 2>/dev/null +return 0 + + diff --git a/zen/park4night_MONITOR.sh b/zen/park4night_MONITOR.sh new file mode 100755 index 0000000..04bc417 --- /dev/null +++ b/zen/park4night_MONITOR.sh @@ -0,0 +1,113 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ +# Extract last ads +# Thank you @kimamila for cesium & gchange +# ES backend http://www.elasticsearchtutorial.com/spatial-search-tutorial.html +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized + +mkdir ~/.zen/cache/gchange -p + +ipfsnodeid=$(ipfs id -f='\n') +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) + +CESIUM="https://g1.data.le-sou.org" + +curl -sk ${CESIUM}/user/profile/${g1pub} -o ~/.zen/cache/cesium_profile.json +LON=$(cat ~/.zen/cache/cesium_profile.json | jq '._source.geoPoint.lon') +LAT=$(cat ~/.zen/cache/cesium_profile.json | jq '._source.geoPoint.lat') + +RAD="$1" +[[ ! $RAD ]] && RAD="50km" + +if [[ "$LON" != "null" ]]; then +curl -sk -XPOST 'https://data.gchange.fr/market/record/_search?pretty&_source=title' -d ' + { + "size": 200, + "query": { + "bool": { + "filter": [{ + "geo_distance": { + "distance": "'$RAD'", + "geoPoint": { + "lat": '$LAT', + "lon": '$LON' + } + } + }] + } + } + }' > /tmp/gchange.json || exit 1 +else + echo "Aucune coordonnées geoPoint pour $g1pub" + sbotc publish '{"type":"post","text":"Ajouter sa géolocalisation dans Cesium+ permet de publier les annonces autour de chez soi..."}' + exit 1 +fi +TIMEBEFORE=$(date -u --date="-$DELAY" +"%s") +TIMESTAMP=$(date -u +"%s") +TOTAL=$(cat /tmp/gchange.json | jq .hits.total) +echo 'tail -f ~/.zen/cache/gchange.txt' +echo 'Annonces_Gchange' > ~/.zen/cache/gchange.txt +echo "Portefeuille_[June_:heart:](https://demo.cesium.app/#/app/wot/$g1pub/)" >> ~/.zen/cache/gchange.txt +echo "Carte_[$RAD](https://www.openstreetmap.org/#map=10/$LAT/$LON) " >> ~/.zen/cache/gchange.txt +chunk=0 +fullcount=0 + +DUNITERNODE=$($MY_PATH/tools/duniter_getnode.sh) +DUNITERURL="https://$DUNITERNODE" +LASTDU=$(curl -s ${DUNITERURL}/blockchain/with/ud | jq '.result.blocks[]' | tail -n 1); +[[ $LASTDU != "" ]] && LASTDU=$(curl -s ${DUNITERURL}/blockchain/block/${LASTDU} | jq '.dividend') +echo "DU = $LASTDU G1" + +for gID in $(cat /tmp/gchange.json | jq -r .hits.hits[]._id); do + + NEW="" + + [[ ! -f ~/.zen/cache/gchange/$gID.json ]] && + NEW="true" \ + && curl -s --create-dirs -o ~/.zen/cache/gchange/$gID.json -s https://data.gchange.fr/market/record/$gID?_source=category,title,description,issuer,time,creationTime,location,address,city,price,unit,currency,thumbnail._content_type,thumbnail._content,picturesCount,type,stock,fees,feesCurrency,geoPoint \ + && sleep $((1 + RANDOM % 3)) + + type=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.type) + stock=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.stock) + [[ $stock == 0 ]] && continue + + # [[ $type == "need" ]] && continue + creationTime=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.creationTime) + title=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.title) + + currency=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.currency) + price=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.price) + + categoryname=$(cat ~/.zen/cache/gchange/$gID.json | jq -r ._source.category.name) + + [[ $price == null ]] && price="0" + [[ $currency == "g1" ]] && love=$(bc -l <<< "scale=2; $price / $LASTDU * 100") || love="?.??" + love="$love_LOVE" + price=$(bc -l <<< "scale=2; $price / 100") + + fullcount=$((fullcount+1)) && echo "DEBUG : $fullcount - $type - $price $currency - $title " + [[ $price == "0" ]] && love="..." && price="A débattre " + + + [[ $type == "offer" ]] && LINE="___OFFRE___[$title](https://data.gchange.fr/market/record/$gID/_share)_$love" + [[ $type == "need" ]] && LINE="__DEMANDE__[$title](https://data.gchange.fr/market/record/$gID/_share)_$love" + + [[ $NEW == "true" ]] && echo "$LINE" >> ~/.zen/cache/gchange.txt && chunk=$((chunk+1)) && echo $chunk + +done +echo "$chunk_nouvelles_annonces_($TOTAL)" >> ~/.zen/cache/gchange.txt + +## TODO AUTOMATIC PUBLISHING \n and message size problem ?? +if [[ $(cat ~/.zen/cache/gchange.txt | wc -c) -lt 8000 ]]; then + export raw="$(cat ~/.zen/cache/gchange.txt)" + annonces=$(node -p "JSON.stringify(process.env.raw)") + sbotc publish '{"type":"post","text":'$annonces'}' +fi +# EXTRA COULD CREATE IT'S OWN MAP with https://github.com/zicmama/tile-stitch.git +# And magick to overlay... But best would be a local map proxy... diff --git a/zen/pkg/make_G1SSB_secret.tar.gz b/zen/pkg/make_G1SSB_secret.tar.gz new file mode 100644 index 0000000..3e87e4e Binary files /dev/null and b/zen/pkg/make_G1SSB_secret.tar.gz differ diff --git a/zen/port_SSH_one2one.sh b/zen/port_SSH_one2one.sh new file mode 100755 index 0000000..1eb1c9f --- /dev/null +++ b/zen/port_SSH_one2one.sh @@ -0,0 +1,32 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ +# Activate SUPPORT MODE: open ssh over IPFS +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" +WHOAMI=$(sbotc whoami | jq -r .id) || er+=" sbotc whoami problem" +[[ "$YOU" == "" || "$IPFSNODEID" == "" || "$WHOAMI" == "" ]] && echo "ERROR : $er " && exit 1 +######################################################################## +# TODO ESTABLISH A PORT FORWARD STRATEGY (depending on Node Flavour) + +# Arrange local port forwarded to swarm + +# GET _uidna (means g1sms/init.sh been run) +[[ -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && UIDNA=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +# G1sms SSB ID "@t+KD4vY/aLlNjWur6LfIEUqb06+E4cZNg3rokOqbFz0=.ed25519" +# IPFS ID : QmVywXoBSz7JZ5vunYYVwi72SdTizvFt7k7qd3ooyYHvHA +[[ "$IPFSNODEID" == "QmVywXoBSz7JZ5vunYYVwi72SdTizvFt7k7qd3ooyYHvHA" ]] && ipfs p2p listen /x/g1pub-ssh /ip4/127.0.0.1/tcp/22 # or CLOSE ipfs p2p close /x/g1pub-ssh + + +if [[ "$IPFSNODEID" == "QmVywXoBSz7JZ5vunYYVwi72SdTizvFt7k7qd3ooyYHvHA" ]]; then + # G1Pub : QmVywXoBSz7JZ5vunYYVwi72SdTizvFt7k7qd3ooyYHvHA + # I am a g1sms NODE, pushing my web interface + ipfs p2p listen /x/g1pub-ssh /ip4/127.0.0.1/tcp/22 +fi diff --git a/zen/port_ipfsp2p_g1sms_all.sh b/zen/port_ipfsp2p_g1sms_all.sh new file mode 100755 index 0000000..0abe59d --- /dev/null +++ b/zen/port_ipfsp2p_g1sms_all.sh @@ -0,0 +1,37 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 0.1 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +################################################################################ +# Activate SUPPORT MODE: open ssh over IPFS +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) || er+=" ipfs daemon not running" +IPFSNODEID=$(ipfs id -f='\n') || er+=" ipfs id problem" +WHOAMI=$(sbotc whoami | jq -r .id) || er+=" sbotc whoami problem" +[[ "$YOU" == "" || "$IPFSNODEID" == "" || "$WHOAMI" == "" ]] && echo "ERROR : $er " && exit 1 +######################################################################## +# TODO ESTABLISH A PORT FORWARD STRATEGY (depending on Node Flavour) + +# Arrange local port forwarded to swarm + +# GET _uidna (means g1sms/init.sh been run) +[[ -f /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && UIDNA=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +if [[ $(which gammu) ]]; then + # I am a g1sms NODE, pushing my web interface + ipfs p2p listen /x/g1sms /ip4/127.0.0.1/tcp/10099 +else + # Looking for g1sms NODE in my swarm + SMSNODE=$(ls /home/$YOU/.zen/ipfs_swarm/.12D3KooW*/G1SSB/_g1sms | shuf -n 1 | cut -d '/' -f 6 | cut -d '.' -f 2) + # Wait for DHT to propagate.... before getting eventual /x/g1sms forward + [[ $SMSNODE ]] && sleep $((1 + RANDOM % 10)) && ipfs p2p forward /x/g1sms /ip4/127.0.0.1/tcp/10097 /p2p/$SMSNODE +fi + +# ipfs p2p close --all +# ipfs p2p listen /x/ssh-$UIDNA /ip4/127.0.0.1/tcp/22 +# ipfs p2p listen /x/http-$UIDNA /ip4/127.0.0.1/tcp/80 +# ipfs p2p listen /x/https-$UIDNA /ip4/127.0.0.1/tcp/443 +ipfs p2p ls diff --git a/zen/ssb_GET_zenyta.sh b/zen/ssb_GET_zenyta.sh new file mode 100755 index 0000000..0123f50 --- /dev/null +++ b/zen/ssb_GET_zenyta.sh @@ -0,0 +1,107 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.24 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# #zenyta = youtube-dl video to ~/.zen/miam/$timestamp +######################################################################## +' +######## YOUTUBE-DL ########## +if [[ ! $(which youtube-dl) ]]; then + sudo curl -s https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl || err=1 + sudo chmod a+rx /usr/local/bin/youtube-dl + sudo chown $(whoami) /usr/local/bin/youtube-dl +fi + +mkdir -p ~/.zen/miam/ + +self=$(sbotc whoami | jq -r .id) || exit 1 +[[ "$self" == "" ]] && exit 1 +g1self=$(echo $self | cut -d '@' -f 2 | cut -d '.' -f 1 | base64 -d | base58) +self_name=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$self"'", "content":{"type":"about", "about": "'"$self"'"}}}}]}' | jq -r .value?.content?.name | grep -v null | tail -n 1) +ipfsnodeid=$(ipfs id -f='\n') + +current_ts=$(date -u +%s%N | cut -b1-13) +[ -s ~/.zen/zenyta.last.ts ] && last_ts=$(cat ~/.zen/zenyta.last.ts) || last_ts=$((current_ts - 24*3600*1000)) # 24h ago +last_ts=$((last_ts + 10)) + +echo "Hi, i am [$self_name]($self) +last timestamp: $last_ts" +echo ' +a u d i o + _ _ + __ _ _ _ __| | (_) ___ +/ _` | | || | / _` | | | / _ \ +\__,_| \_,_| \__,_| |_| \___/ + +' + + +#messages=$(sbotc messagesByType '{"type":"post","gt":'$last_ts'}') +# SEARCH "#zenyta" CMD in message text +echo "sbotc backlinks.read '{\"query\":[{\"\$filter\":{\"dest\":\"#zenyta\",\"value\":{\"content\":{\"type\":\"post\"}},\"timestamp\":{\"\$gt\":'\"$last_ts\"'}}}]}'" +messages=$(sbotc backlinks.read '{"query":[{"$filter":{"dest":"#zenyta","value":{"content":{"type":"post"}},"timestamp":{"$gt":'"$last_ts"'}}}]}') +[[ $messages == "" ]] && messages=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$WHOAMI"'", "content":{"type":"zenyta"}}}}]}') && echo "sbotc query.read '{\"query\":[{\"\$filter\":{\"value\":{\"author\": \"'"$WHOAMI"'\", \"content\":{\"type\":\"zenyta\"}}}}]}'" +while read -r msg +do + # EXTRACT CMD PARAM + msg_key=$(printf %s "$msg" | jq -r .key) + author=$(printf %s "$msg" | jq -r .value.author) + timestamp=$(printf %s "$msg" | jq -r .value.timestamp) + + # TEST CURRENT NODE last_ts + [[ $timestamp -lt $last_ts ]] && echo "$last_ts: ALREADY DONE $msg_key timestamp is $timestamp " && continue + + # SWARM REFRESH + $MY_PATH/ipfs_SWARM_refresh.sh + # SWARM ALREADY DONE IT ?? + # IS LIKE "SELECT FROM ipfs_swarm WHERE _tag.zensource=$timestamp"(or something like that in SQL), means a ZenTag already exists. + CHECKSWARM=$(grep -Rwl "$timestamp" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/') + # OR SWARM PROCESS IN ACTION (NB: copy tasks must be asynchronous in swarm) + [[ ! $CHECKSWARM ]] && CHECKSWARM=$(grep -Rwl "$timestamp" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/process.timestamp.ssb | tail -n 1 | cut -f 6 -d '/') + [[ $CHECKSWARM ]] \ + && echo "$timestamp ALREADY on NODE $CHECKSWARM CONTINUE" \ + && echo "$timestamp" > ~/.zen/zenyta.last.ts \ + && continue + + # ANTI DOUBLE COPY START + echo "$timestamp" > ~/.zen/ipfs/.$ipfsnodeid/process.timestamp.ssb + $MY_PATH/ipfs_SWARM_refresh.sh + # ANTI DOUBLE COPY + + msg_root=$(printf %s "$msg" | jq -r .value.content.root) + msg_branch=$(printf %s "$msg" | jq -r .value.content.branch) + msg_text=$(printf %s "$msg" | jq -r .value.content.text) + msg_ytdlurl=$(echo $msg_text | egrep -o 'https?://[^ ]+' | grep you | cut -d '\' -f 1 | tail -n 1) + + # YOUTUBE-DL AUDIO + +[[ $timestamp ]] && mkdir -p ~/.zen/miam/$timestamp + +[[ $msg_ytdlurl ]] && /usr/local/bin/youtube-dl -x --audio-format mp3 \ +--write-thumbnail --write-info-json --add-metadata --embed-thumbnail \ +--no-mtime -o "~/.zen/miam/$timestamp/%(id)s.%(ext)s" $msg_ytdlurl + + # REFERENCE msg_key AND timestamp + [[ $timestamp ]] && echo "$((timestamp + 1))" > ~/.zen/zenyta.last.ts + [[ $msg_key ]] && echo "$msg_key" > ~/.zen/miam/$timestamp/msg_key + + $MY_PATH/miam_miam.sh "$timestamp" + + # ANTI DOUBLE COPY END + rm ~/.zen/ipfs/.$ipfsnodeid/process.timestamp.ssb + $MY_PATH/ipfs_SWARM_refresh.sh + # ANTI DOUBLE COPY + +done < <(printf '%s\n' "$messages") diff --git a/zen/ssb_GET_zenytv.sh b/zen/ssb_GET_zenytv.sh new file mode 100755 index 0000000..af4efdf --- /dev/null +++ b/zen/ssb_GET_zenytv.sh @@ -0,0 +1,109 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.24 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# #zenytv = youtube-dl video to ~/.zen/miam/$timestamp +######################################################################## + +######## YOUTUBE-DL ########## +if [[ ! $(which youtube-dl) ]]; then + sudo curl -s https://yt-dl.org/downloads/latest/youtube-dl -o /usr/local/bin/youtube-dl || err=1 + sudo chmod a+rx /usr/local/bin/youtube-dl + sudo chown $(whoami) /usr/local/bin/youtube-dl +fi + +mkdir -p ~/.zen/miam/ + +self=$(sbotc whoami | jq -r .id) || exit 1 +[[ "$self" == "" ]] && exit 1 +g1self=$(echo $self | cut -d '@' -f 2 | cut -d '.' -f 1 | base64 -d | base58) +self_name=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$self"'", "content":{"type":"about", "about": "'"$self"'"}}}}]}' | jq -r .value?.content?.name | grep -v null | tail -n 1) +ipfsnodeid=$(ipfs id -f='\n') + +current_ts=$(date -u +%s%N | cut -b1-13) +[ -s ~/.zen/zenytv.last.ts ] && last_ts=$(cat ~/.zen/zenytv.last.ts) || last_ts=$((current_ts - 24*3600*1000)) # 24h ago +# last_ts=$((last_ts + 10)) + +echo "Hi, i am [$self_name]($self) +last timestamp: $last_ts" +echo ' +v i d e o + _ _ +__ __ (_) __| | ___ ___ +\ V / | | / _` | / -_) / _ \ + \_/ |_| \__,_| \___| \___/ + +' + + +#messages=$(sbotc messagesByType '{"type":"post","gt":'$last_ts'}') +# SEARCH "#zenytv" CMD in message text +echo "sbotc backlinks.read '{\"query\":[{\"\$filter\":{\"dest\":\"#zenytv\",\"value\":{\"content\":{\"type\":\"post\"}},\"timestamp\":{\"\$gt\":'\"$last_ts\"'}}}]}'" +messages=$(sbotc backlinks.read '{"query":[{"$filter":{"dest":"#zenytv","value":{"content":{"type":"post"}},"timestamp":{"$gt":'"$last_ts"'}}}]}') +[[ $messages == "" ]] && exit 1 +while read -r msg +do + # EXTRACT CMD PARAM + msg_key=$(printf %s "$msg" | jq -r .key) + author=$(printf %s "$msg" | jq -r .value.author) + timestamp=$(printf %s "$msg" | jq -r .value.timestamp) + + # TEST CURRENT NODE last_ts + [[ $timestamp -lt $last_ts ]] && echo "ALREADY DONE $msg_key timestamp is $timestamp " && continue + + # SWARM REFRESH + $MY_PATH/ipfs_SWARM_refresh.sh + # SWARM ALREADY DONE + CHECKSWARM=$(grep -Rwl "$timestamp" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/') + # OR SWARM PROCESS IN ACTION + [[ ! $CHECKSWARM ]] && CHECKSWARM=$(grep -Rwl "$timestamp" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/process.timestamp.ssb | tail -n 1 | cut -f 6 -d '/') + [[ $CHECKSWARM ]] \ + && echo "$timestamp ALREADY on NODE $CHECKSWARM CONTINUE" \ + && echo "$timestamp" > ~/.zen/zenytv.last.ts \ + && continue + + # ANTI DOUBLE COPY START + echo "$timestamp" > ~/.zen/ipfs/.$ipfsnodeid/process.timestamp.ssb + $MY_PATH/ipfs_SWARM_refresh.sh + # ANTI DOUBLE COPY + + msg_root=$(printf %s "$msg" | jq -r .value.content.root) + msg_branch=$(printf %s "$msg" | jq -r .value.content.branch) + msg_text=$(printf %s "$msg" | jq -r .value.content.text) + msg_ytdlurl=$(echo $msg_text | egrep -o 'https?://[^ ]+' | grep you | cut -d '\' -f 1 | tail -n 1) + + # YOUTUBE-DL VIDEO + +[[ $timestamp ]] && mkdir -p ~/.zen/miam/$timestamp + +[[ $msg_ytdlurl ]] && /usr/local/bin/youtube-dl -f '[height=720]/best' \ +--write-thumbnail --all-subs --write-info-json --write-annotations \ +--no-mtime -o "~/.zen/miam/$timestamp/%(id)s.%(ext)s" $msg_ytdlurl + +# ###### TODO make gif tu push to SSB +# ffmpeg -ss 00:00:00.000 -i yesbuddy.mov -pix_fmt rgb24 -r 10 -s 320x240 -t 00:00:10.000 output.gif +# convert -layers Optimize output.gif output_optimized.gif + + # REFERENCE msg_key AND timestamp + [[ $timestamp ]] && echo "$((timestamp))" > ~/.zen/zenytv.last.ts + [[ $msg_key ]] && echo "$msg_key" > ~/.zen/miam/$timestamp/msg_key + + $MY_PATH/miam_miam.sh "$timestamp" + + # ANTI DOUBLE COPY END + echo "$timestamp" > ~/.zen/ipfs/.$ipfsnodeid/process.timestamp.ssb + $MY_PATH/ipfs_SWARM_refresh.sh + # ANTI DOUBLE COPY + +done < <(printf '%s\n' "$messages") diff --git a/zen/ssb_INIT.sh b/zen/ssb_INIT.sh new file mode 100755 index 0000000..76f6510 --- /dev/null +++ b/zen/ssb_INIT.sh @@ -0,0 +1,260 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.21 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# Initialize G1SSB + SSB About + IPFS Node publish +######################################################################## + o__ __o __o o__ __o o__ __o o__ __o + /v v\ __|> /v v\ /v v\ <| v\ + /> <\ | /> <\ /> <\ / \ <\ + o/ _\o____ _\o____ \o/ o/ + <| _\__o__ | \_\__o__ \_\__o__ |__ _<| + \\ | < > \ \ | \ + \ / | \ / \ / / + o o o o o o o | o + <\__ __/> __|>_ <\__ __/> <\__ __/> / \ __/> + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# [ASTROPORT](https://astroport.com) +######################################################################## +' + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1); +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 +######################################################################## +WHOAMI=$(sbotc whoami 2>/dev/null | jq -r .id) +[[ $WHOAMI == "" ]] && echo "ERROR sbotc NOT running !! Please check it..." && exit 1 +######################################################################## +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +G1PUB=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +[[ $G1PUB == "" ]] && echo "ERROR G1PUB empty !! Please check it..." && exit 1 +######################################################################## + + +# GET NODE disk performance. TODO, publish and use as IPFS repartition +echo "DISK SIZE AVAILABLE & PERFORMANCE TESTING" +[[ -f ~/.zen/test.disk ]] && rm -f ~/.zen/test.disk +diskperf=$(dd if=/dev/zero of=~/.zen/test.disk bs=10M count=1 oflag=dsync 2>&1 | tail -n 1 | sed s/\,\ /\ -/g | cut -d '-' -f 4) +# echo $diskperf +sizeAvail=$(df -h ~/.ipfs/ | tail -n 1 | awk '{print $4}') + + +# IPFS LOCAL REPOSITORY for Node Identity G1 + SSB +mkdir -p ~/.zen/ipfs/.$IPFSNODEID/G1SSB + +# SSB PUBLISH EXTRA ipfs informations !! +# TODO CHECK FOR NOT MAKING DOUBLE PUBLICATION (lower noisy init) + +# This SSB messages are read by ./zen/ssb_IPFS_swarm.sh to build your IPFS #Swarm0 +sbotc publish '{"type":"ipfsnodeid","text":"'"$(ipfs id -f='\n')"'"}' + +# PUBLISH default "eth" NOT isLAN IP addresses for ./zen/ssb_IPFS_swarm.sh +tryme=$(ipfs id | jq -r .Addresses[] | tail -n 1 ) +isLAN=$(echo $tryme | cut -f3 -d '/' | grep -E "/(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") +[[ ! $isLAN ]] && sbotc publish '{"type":"ipfstryme","text":"'"$tryme"'"}' + +trymev4=$(ipfs id | jq -r .Addresses[] | grep $(hostname -I | cut -f 1 -d ' ')) +isLANv4=$(echo $trymev4 | cut -f3 -d '/' | grep -E "/(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") +[[ $isLAN && ! $isLANv4 ]] && sbotc publish '{"type":"ipfstryme","text":"'"$trymev4"'"}' && tryme="$trymev4" + +trymev6=$(ipfs id | jq -r .Addresses[] | grep $(hostname -I | cut -f 2 -d ' ')) +isLANv6=$(echo $trymev6 | cut -f3 -d '/' | grep -E "/(^127\.)|(^192\.168\.)|(^fd42\:)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") +[[ $isLAN && $isLANv4 && ! $isLANv6 ]] && sbotc publish '{"type":"ipfstryme","text":"'"$trymev6"'"}' && tryme="$trymev6" + +################################ +# ADD Cesium+ informations +CESIUM="https://g1.data.le-sou.org" + +# PREPARE title +# Made from Cesium+ profile tittle and city OR user@hostname +title=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.title') +[[ -f ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna ]] && uidna=$(cat ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_uidna) + +# Put in .$IPFSNODEID INDEX: _g1.uidna & _g1.cesium_name (used by Minetest flavour and others) +[[ $uidna ]] && echo "$uidna" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.uidna +[[ $title ]] && echo "$title" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.cesium_name + +[[ $uidna ]] && [[ "$title" == "null" ]] && title="Station $uidna" +[[ "$title" == "null" ]] && title="Station $USER@$(cat /etc/hostname)" + +city=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.city') +[[ "$city" != "null" ]] && title="$title in $city" + + +# ADD "cesium_geoPoint.lat" AND "cesium_geoPoint.lon" messages in SSB feed +# This way any SSB account is connected to its Cesium+ Geolocalisation. +geopointlat=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq '._source.geoPoint.lat') +[[ $geopointlat != null ]] && sbotc publish '{"type":"cesium_geoPoint.lat","text":"'"$geopointlat"'"}' +geopointlon=$(curl -s ${CESIUM}/user/profile/${G1PUB} | jq '._source.geoPoint.lon') +[[ $geopointlon != null ]] && sbotc publish '{"type":"cesium_geoPoint.lon","text":"'"$geopointlon"'"}' + +# REFRESH Cesium+ Avatar image +curl -s ${CESIUM}/user/profile/${G1PUB} | jq -r '._source.avatar._content' | base64 -d > "/tmp/_g1.avatar.png" + +## PUBLISH ABOUT MESSAGE +############################################## +# PICTURE: IF AVATAR not png take IMAGE of G1 wallet QRCode +qrencode -s 5 -o ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png "$G1PUB" +imagefile=~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png +qrname=${imagefile##*/} +qrid="$(sbotc blobs.add < $imagefile)" +qrtype="$(file -b --mime-type $imagefile)" +qrsize="$(wc -c < $imagefile)" + +if [[ ! $(file "/tmp/_g1.avatar.png" | grep 'PNG') ]]; then + echo "NO Cesium AVATAR - Using G1Pub QRCode" + rm -f /tmp/_g1.avatar.png +else + echo "AVATAR FOUND" + imagefile=/tmp/_g1.avatar.png + # PUBLISH AVATAR TO IPFS + cp /tmp/_g1.avatar.png ~/.zen/ipfs/.$IPFSNODEID/G1SSB/ +fi + +# Prepare QRCode File for SSB (add to blobs) +name=${imagefile##*/} +id="$(sbotc blobs.add < $imagefile)" +type="$(file -b --mime-type $imagefile)" +size="$(wc -c < $imagefile)" + +echo " + + /\ |_ _ _|_ +/--\|_)(_)|_||_ : PUBLISH to SSB feed... + +$WHOAMI +$title +$imagefile +$id : $type : $size bits + +" +# NOT WORKING, sudo inside !!! +#nodename=$(~/.zen/astroport/zen/tools/nodename) +nodename=$(cat /home/$YOU/.zen/ipfs/.$IPFSNODEID/G1SSB/_nodename) +if [[ $nodename == "" ]]; then + nodename=$(cat /etc/hostname) + extension=$(echo $nodename | cut -d '.' -f 2) + if [[ $extension == $nodename ]]; then + nodename=$nodename.home + fi +fi +######################################################################## +# DUNITER G1 Wallet balance +export LC_ALL=C.UTF-8 #attipix +export LANG=C.UTF-8 #attipix +DUNITERNODE=$($MY_PATH/tools/duniter_getnode.sh) +echo "DEBUG: silkaj -p $DUNITERNODE balance $G1PUB" +[[ $DUNITERNODE ]] && g1balance=$(silkaj -p $DUNITERNODE balance $G1PUB 2>&1) || g1balance=$(silkaj balance $G1PUB 2>&1) +silkajQuantitativeAmountPattern='Total\sQuantitative\s+=\s+(.*)\s+Ğ1' +if [[ $g1balance =~ $silkajQuantitativeAmountPattern ]] +then + myJune="${BASH_REMATCH[1]}" +else + myJune="0" +fi + +DUFACTOR=$(bc <<< "scale=2; $(cat /home/$YOU/.zen/_DU) / 100") +AMOUNTLOVE=$(bc -l <<< "scale=0; $myJune * 100 / $DUFACTOR") +# OLD ssb-server publish +# sbot publish --type about --about $WHOAMI --description "[Astroport Node](https://astroport.com) [$IPFSNODEID](http://localhost:8080/ipns/$IPFSNODEID) - Wallet $G1PUB - $diskperf" --name "$title" --image "$id" +# NEW sbotc publish to oasis +sbotc publish "{\"type\":\"about\",\"about\":\"$WHOAMI\",\"description\":\"![QRCode]($qrid)\\n[Station Astroport](https://astroport.com)\\n - [Mon Marché](https://gchange.fr/#/app/wot/$G1PUB) \\n - [Mon portefeuille ($AMOUNTLOVE :heart:)](https://cesium.app/) \\n Station ID : [$IPFSNODEID](http://localhost:8080/ipns/$IPFSNODEID) \\n Disque: $sizeAvail = $diskperf \\n - [Portail](http://$nodename:10010/) \\n\",\"name\":\"$title\",\"image\":\"$id\"}" + + +# SSB PUBLISH G1 wallet silkaj balance +json_escape () { + printf '%s' "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' +} +INLINE=$(json_escape "$g1balance") +# TODO FIND WHY THIS ***** COMA , IS EVERYWHERE NOT PUBLSHING silkaj +# [[ $INLINE ]] && sbotc publish '{"type":"post","text":'$INLINE'}' + +#INLINE="${g1balance@Q}" +#[[ $INLINE ]] && sbotc publish '{"type":"post","text":"'$INLINE'"}' + +echo " + _ _ +/ \|_) _ _ _| _ +\_X| \ (_(_)(_|(/_ ! AVATAR + +$g1balance + +~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.qrcode.png + + -- sbotc publish -- +" +# IF no AVATAR, publish message with QRCode +if [[ ! $(file "/tmp/_g1.avatar.png" | grep 'PNG') ]]; then + sleep 1 +# sbotc publish '{"type":"post","text":"![QRCode]('"$qrid"')\n Scan QRCode with [Cesium](https://cesium.app).\n Thank you\n ONE :heart:","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' +else + # Publish only if new avatar + if [[ $(diff /tmp/_g1.avatar.png ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.avatar.png) ]]; then + sbotc publish '{"type":"post","text":"![Cesium Avatar]('"$id"')\n from my Wallet [Cesium](https://demo.cesium.app/#/app/wot/'"$G1PUB"') '"$G1PUB"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' + fi +fi + +echo " + + +___ _ _ __ + | |_)|_(_ _. _| _| +_|_| | __) (_|(_|(_| + ~/.zen/ipfs + +ipfs ls /ipns/$IPFSNODEID + +" +# COPY NODE G1SSB ID to IPFS +echo "$WHOAMI" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_ssb.whoami +echo "$G1PUB" > ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_g1.pubkey + +# IPFS Node PUBLISH Adresses so Pub can become bootstrap for ${g1author} +ipfs id | jq -r .Addresses[] > ~/.zen/ipfs/.${IPFSNODEID}/Addresses +# IPFS Node PUBLISH AgentVersion & repo.stat +ipfs id | jq -r .AgentVersion > ~/.zen/ipfs/.${IPFSNODEID}/AgentVersion +ipfs repo stat > ~/.zen/ipfs/.${IPFSNODEID}/repo.stat + +echo "$tryme" > ~/.zen/ipfs/.${IPFSNODEID}/tryme.addr +echo "$diskperf" > ~/.zen/ipfs/.${IPFSNODEID}/disk.perf +echo $(df ~/.ipfs/ | tail -n 1 | awk '{print $4}') > ~/.zen/ipfs/.${IPFSNODEID}/disk.bytes + + +IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --allow-offline --quieter /ipfs/$IWALLETS) + +echo " +ipfs ls /ipns/$IPFSNODEID/.$IPFSNODEID/ + _ _ _ _ _ +(_)_ ____ _(_) |_ __ _| |_(_) ___ _ __ +| | _ \ \ / / | __/ _| | __| |/ _ \| _ \ +| | | | \ V /| | || (_| | |_| | (_) | | | | +|_|_| |_|\_/ |_|\__\__|_|\__|_|\___/|_| |_| + +# This INVITE is to be sent an to 'Astroport Station' willing to Join our IPFS Swarm. +# see 'ssb_SURVEY_contact.sh' for commands executed... + +" +echo "INVITATION LINK (only works in LAN or WAN depending on your Node)" +sbotc invite.create 1 2>/dev/null + +#read ssb_invit_link +#sbotc invite.accept $ssb_invit_link + +exit 0 diff --git a/zen/ssb_IPFS_swarm.sh b/zen/ssb_IPFS_swarm.sh new file mode 100755 index 0000000..d12f9ed --- /dev/null +++ b/zen/ssb_IPFS_swarm.sh @@ -0,0 +1,99 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.04.27 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# GET SSB FRIENDS AND FIND THEIR IPFS ID TO "ipfs swarm connect" THEM +########################################################################' + +## TODO: REMOVE DUPLICATES +## TODO: MODE : COOL, STRAIGHT, ARMORED + +######################################################################## +## CONNECT GLOBAL "ipfs.io" ## DANGEROUS only for short time... +######################################################################## +# ADD ipfs.io public bootstrap into your swarm peers +# RUN: cat ~/.zen/astroport/ipfs.swarm.ipfs.io | ipfs swarm connect +# SOON ipfs swarm peers will GROW!!! YOU ARE VSIBLE !!! +# RUN: sudo systemctl restart ipfs # GOES BACK TO SWARM0 +######################################################################## + +######################################################################## +# ENVIRONEMENT DETECTION + IPFS ~/.zen/ipfs/.$IPFSNODEID/G1SSB/_info +######################################################################## +IPFSNODEID=$(ipfs id -f='\n') +[[ $IPFSNODEID == "" ]] && echo "ERROR missing IPFS Node id !! IPFS is not installed !?" && exit 1 +######################################################################## + +echo ' + __ __ ____ + __/ // /_______ ______ __________ ___ / __ \ + /_ _ __/ ___/ | /| / / __ `/ ___/ __ `__ \/ / / / +/_ _ __(__ )| |/ |/ / /_/ / / / / / / / / /_/ / + /_//_/ /____/ |__/|__/\__,_/_/ /_/ /_/ /_/\____/ + + +EXTEND IPFS SWARM and SHAPE IT FROM (ssb_INIT.sh) FRIENDS +Search "ipfstryme" message type in SSB feed +' +[[ ! $WHOAMI ]] && WHOAMI=$($MY_PATH/tools/timeout.sh -t 3 sbotc whoami | jq -r .id) +######################################################################## + +# GET /tmp/ssb-friends.txt +sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$WHOAMI"'", "content":{"type":"contact"}}}}]}' | jq -r '.value?.content?.contact' > /tmp/ssb-friends.txt + +# GET /tmp/ssb-NOTfriends.txt +sbotc links "{\"source\": \"${WHOAMI}\", \"rel\": \"contact\", \"values\": true, \"reverse\": true}" | jq -c . | grep 'blocking":true' | jq -r .dest > /tmp/ssb-NOTfriends.txt + +count=1 +######################################################################## +# Let's look if our SSB Friends ARE "IPFS swarm connected" +######################################################################## +echo "" > /tmp/This_NOTfriends_are_astroport +echo "" > /tmp/This_friends_connect_astroport +echo "" > /tmp/This_friends_should_install_astroport +### TODO -> send sbotc message (private or zip attached?) with this reports + +for SSBFRIEND in $(uniq /tmp/ssb-friends.txt); do + + # Force Strict #swarm0 !! + [[ $count == 1 ]] && ipfs bootstrap rm --all + + ### sbotc $SSBFRIEND name + MYFRIEND=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$SSBFRIEND"'", "content":{"type":"about", "about": "'"$SSBFRIEND"'"}}}}]}' | jq -r .value?.content?.name | grep -v null | tail -n 1) + ### GET SSB "ipfstryme" message type !!! Astroport Node should have publish it during "ssb_INIT.sh" + TRYME=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$SSBFRIEND"'", "content":{"type":"ipfstryme"}}}}]}' | jq -r .value?.content?.text | tail -n 1) + + ## !! REMOVE NOTfriends from IPFS swarm + [[ $TRYME ]] && [[ $(grep -Rwl "$SSBFRIEND" /tmp/ssb-NOTfriends.txt) ]] && MES="($count) HUMMMM $MYFRIEND ($SSBFRIEND) IS NOT my friend disconnecting" && ipfs swarm disconnect $TRYME && ipfs bootstrap rm $TRYME && echo $MES >> /tmp/This_NOTfriends_are_astroport && continue + ## Ici, on peut décider de demander à faire supprimer la couche astroport à son PAS AMI + + ## ADD Friend to our IPFS swarm + [[ $TRYME ]] && MES="($count) $MYFRIEND ($SSBFRIEND) connect OK $TRYME" && ipfs swarm connect $TRYME && ipfs bootstrap add $TRYME && echo $MES >> /tmp/This_friends_connect_astroport + ## Ce pote est connecté IPFS avec moi + + ## This_friends_should_install_astroport + [[ ! $TRYME ]] && MES="($count) $MYFRIEND ($SSBFRIEND) is NOT running ASTROPORT !!!" && echo $MES >> /tmp/This_friends_should_install_astroport + + ### WHAT HAPPENED this loop on my ssb friends + echo $MES + echo "_________________________________________________" + count=$((count+1)) + +done + + +echo "__________________________________________ +$WHOAMI ipfs peers are:" +ipfs swarm peers diff --git a/zen/ssb_SURVEY_contact.sh b/zen/ssb_SURVEY_contact.sh new file mode 100755 index 0000000..3d6eaf6 --- /dev/null +++ b/zen/ssb_SURVEY_contact.sh @@ -0,0 +1,134 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.24 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# SURVEY SSB contact from "invite" is happening in feed +######################################################################## _ _ _ _ _ +(_)_ ____ _(_) |_ __ _| |_(_) ___ _ __ +| | _ \ \ / / | __/ _| | __| |/ _ \| _ \ +| | | | \ V /| | || (_| | |_| | (_) | | | | +|_|_| |_|\_/ |_|\__\__|_|\__|_|\___/|_| |_| contact SURVEY + +# New Station is joining ASTROPORT !! + +DETECT in SSB feed messages "type": "contact", "autofollow": true + +LIKE +https://tube.p2p.legal/videos/watch/ef319fdd-caf1-4e03-ba22-91c456e94f73 +{ + "key": "%jVQewn0/ey0tfdvYCjYXJqVivaaZ6NpeUL9xK5QeGTk=.sha256", + "value": { + "previous": "%ilrZ+8CvMXpu09LWh75Uq37j7lVJnoxTvipyooRSjpg=.sha256", + "sequence": 3, + "author": "@S2KB1zRQNAuCRs1vW08c+63+0gjI2egDj4pjUUrt+dw=.ed25519", + "timestamp": 1585068288462, + "hash": "sha256", + "content": { + "type": "contact", + "following": true, + "autofollow": true, + "contact": "@t/giTDc0EtzdPQGC7iAAzgzVOkFo++XZRvzOOlqgX1c=.ed25519" + }, + "signature": "kXsFqfBGqZXZApf7UANDYlqnwLXuGdWFbMMofljBOp4dNGc4dAv+P2hzK3XV/jPT8a1u7PHIraJASugR1NCLCw==.sig.ed25519" + }, + "timestamp": 1585068290213 +} +' +# CACHE +[[ ! -d ~/.zen/ssb_contact ]] && mkdir -p ~/.zen/ssb_contact + +self=$(sbotc whoami | jq -r .id) || exit 1 +g1self=$(echo $self | cut -d '@' -f 2 | cut -d '.' -f 1 | base64 -d | base58) +self_name=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$self"'", "content":{"type":"about", "about": "'"$self"'"}}}}]}' | jq -r .value?.content?.name | grep -v null | tail -n 1) + +messages=$(sbotc query.read '{"query":[{"$filter":{"value":{"content":{"type":"contact", "contact":"'"$self"'", "autofollow":true}}}}]}') +while read -r msg +do + + timestamp=$(printf %s "$msg" | jq .value.timestamp) + +echo ' + _ _ ___ ____ + / / \|\ || /\ / | + \_\_/| \||/--\\_ | FROM NEW STATION' +echo $timestamp + + + author=$(printf %s "$msg" | jq -r .value.author) + [[ "$author" == "$self" ]] && echo "Message from myself" && continue + g1author=$(printf %s "$msg" | jq -r .value.author | cut -d '@' -f 2 | cut -d '.' -f 1 | base64 -d | base58) + + # Crypt, ipfs store, send + if [[ -f ~/.ipfs/swarm.key ]]; then + + ipfsnodeid=$(ipfs id -f='\n') + mkdir -p ~/.zen/ipfs/.${ipfsnodeid}/CONTACT/${g1author} + + # PUBLISH swarm.key.crypt to IPFS, so PUB can push new swam.key to our CONTACT + file=~/.zen/ipfs/.${ipfsnodeid}/CONTACT/${g1author}/ipfs_swarm.key.crypt + + $MY_PATH/tools/natools.py encrypt -p ${g1author} -i ~/.ipfs/swarm.key -o ${file} + + # IPFS Node PUBLISH Adresses so Pub can become bootstrap for ${g1author} + ipfs id | jq -r .Addresses[] > ~/.zen/ipfs/.${ipfsnodeid}/Addresses + # IPFS Node PUBLISH AgentVersion & repo.stat + ipfs id | jq -r .AgentVersion > ~/.zen/ipfs/.${ipfsnodeid}/AgentVersion + ipfs repo stat > ~/.zen/ipfs/.${ipfsnodeid}/repo.stat + + bootstrap=$(cat ~/.zen/ipfs/.${ipfsnodeid}/Addresses | tail -n 1) + +echo " +$author + +\|/ \|/ \|/ +/|\ /|\ /|\ preparing cypher swarmkey + +~/.zen/ipfs/.${ipfsnodeid}/CONTACT/${g1author}/ipfs_swarm.key.crypt + +PUBLISH IPFS +http://localhost:8080/ipns/ +" + ipfs name publish --quieter /ipfs/$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) + + name=${file##*/} + id="$(sbotc blobs.add < "$file")" + type="$(file -b --mime-type "$file")" + size="$(wc -c < "$file")" + +echo ' + ______ _ _ _ ____ __ _ _ + /\ (_ ||_)/ \|_)/ \|_)|__(_\ //\ |_)|\/||/|_\_/ +/--\__) || \\_/| \_/| \| __)\/\//--\| \| ||\|_ | message send +' +echo "SSB +#astroport-swarmkey +[$name]($id) +TO.SSB_${author} +TO.G1_${g1author} ++++ +FROM.SSB_${self_name} +FROM.G1_${g1self} +FROM.IPFS_${ipfsnodeid} + +" + sbotc publish '{"type":"post","text":"#astroport-swarmkey = ['"$name"']('"$id"') TO.SSB_'"$author"' TO.G1_'"$g1author"' +++ FROM.SSB_'"$self_name"' FROM.G1_'"$g1self"' FROM.IPFS_'"$ipfsnodeid"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'"}]}' + + else + + echo "NO swarm.key here." + + fi + +done < <(printf '%s\n' "$messages") diff --git a/zen/ssb_SURVEY_swarmkey.sh b/zen/ssb_SURVEY_swarmkey.sh new file mode 100755 index 0000000..2979e90 --- /dev/null +++ b/zen/ssb_SURVEY_swarmkey.sh @@ -0,0 +1,122 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.24 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# ex: ./'$ME' +# SURVEY received #astroport-swarmkey ipfs_swarm.key.crypt for IPFS +######################################################################## +___DESACTIVATED___ TODO testing. + __ __ _ _ +(_ (_ |_) _ ._ _ _|_o| +__)__)|_) _>|_||\/(/_\/ | ||(/__> + / + +USED ONLY ONCE... Next swarm.key change will accurs in ~/.zen/ipfs-swarm + +{ + "key": "%ydN2fyzKTKzVZiZJSxiTXbb17GVvu0ZOf7LPY6u3Bc8=.sha256", + "value": { + "previous": "%SM2J1D8RcWzd2/P6I3ATyN4VXHuQEP3sXy7YCtK0djE=.sha256", + "sequence": 18, + "author": "@t/giTDc0EtzdPQGC7iAAzgzVOkFo++XZRvzOOlqgX1c=.ed25519", + "timestamp": 1585194822251, + "hash": "sha256", + "content": { + "type": "post", + "text": "#astroport-swarmkey = [ipfs_swarm.key.crypt](&L9nCidDjJ4c4Jz1LNTtx8Xp0SJW9HPCD9IVsbbAhS/I=.sha256) TO.SSB_${author} TO.G1_${g1author} +++ FROM.SSB_${self_name} FROM.G1_${g1self} FROM.IPFS_${ipfsnodeid}", + "mentions": [ + { + "link": "&L9nCidDjJ4c4Jz1LNTtx8Xp0SJW9HPCD9IVsbbAhS/I=.sha256", + "name": "ipfs_swarm.key.crypt", + "size": 144, + "type": "application/octet-stream" + } + ] + }, + "signature": "DHMTIS17yF450CqFssuP2iwYMMdOd3PzCDTkLYdjprTtvjWZYUEG9vHaXBrGuaFZRhV2gGZ3WSknM7YLevilAQ==.sig.ed25519" + }, + "timestamp": 1585194822367 +} +' + +[[ -f ~/.ipfs/ipfs_swarm.key ]] && echo "SWARM KEY ~/.ipfs/ipfs_swarm.key OK !!!" && exit 0 +self=$(sbotc whoami | jq -r .id) || exit 1 +g1self=$(echo $self | cut -d '@' -f 2 | cut -d '.' -f 1 | base64 -d | base58) +self_name=$(sbotc query.read '{"query":[{"$filter":{"value":{"author": "'"$self"'", "content":{"type":"about", "about": "'"$self"'"}}}}]}' | jq -r .value?.content?.name | grep -v null | tail -n 1) +ipfsnodeid=$(ipfs id -f='\n') + +# SEARCH "#astroport-swarmkey" CMD in message text +# Not working without patchwork (TODO: find bug. installation ok!? activate? ssb-server ssb-backlinks node_modules. HELP !! ) +# messages=$(sbotc backlinks.read '{"query":[{"$filter":{"dest":"#astroport-swarmkey","value":{"content":{"type":"post"}}}}]}') +# The backlinks.read command does not publish a message, it queries the database for messages. It comes from the ssb-backlinks plugin. This plugin does not come with ssb-server by default (but it does come with Patchwork) so if you are using plain ssb-server and want to use ssb-backlinks you have to install it additionally. But to publish a message you would use the publish method (or private.publish to publish a private message, and that requires the ssb-private plugin, which is included in Patchwork but must be installed separately for ssb-server). + +# SCRIPT RUN BY cron_MINUTE.sh check last hour messages +current_ts=$(date -u +%s%N | cut -b1-13) +last_ts=$((current_ts - 24*3600*1000 - 1)) # timestamp from 24h ago + +echo ' + _ _ +-|-|- _. __|_.__ ._ _ .__|___/ |\/|| \ +-|-|-(_|_> |_|(_)|_)(_)| |_ \_| ||_/ + | +sbotc messagesByType "post" > $last_ts +' + +messages=$(sbotc messagesByType '{"type":"post","gt":'$last_ts'}') +#messages=$(sbotc messagesByType '{"type":"post"}') #DEBUG + +while read -r msg +do + + author=$(printf %s "$msg" | jq -r .value.author) + attached_file=$(printf %s "$msg" | jq -r .value.content.mentions[].name 2>/dev/null) + + if [[ $attached_file == "ipfs_swarm.key.crypt" ]]; then + + +echo ' + __ _ _ +(_\ //\ |_)|\/| |/|_\_/ _._ .__|_ +__)\/\//--\| \| | |\|_ | de(_|\/|_)|_ + / | +to ~/.ipfs/ipfs_swarm.key +' + + mylink=$(printf %s "$msg" | jq -r .value.content.mentions[].link) + mytmp=$(mktemp -d "${TMPDIR:-/tmp}/astroport.swarmkey.XXXXXXXXX") + +echo "http://localhost:8989/blobs/get/$mylink" +continue + curl -s "http://localhost:8989/blobs/get/$mylink" > $mytmp/ipfs_swarm.key.crypt + $MY_PATH/tools/natools.py decrypt -f pubsec -k ~/.ssb/secret.dunikey -i $mytmp/ipfs_swarm.key.crypt -o ~/.ipfs/ipfs_swarm.key && \ + echo "IPFS SWARM KEY ~/.ipfs/ipfs_swarm.key received from SSB $author ... OK !" + + +echo ' +___ _ _ __ + | |_)|_(_ _| _. _ ._ _ _ ._ +_|_| | __) (_|(_|(/_| | |(_)| | ... restart ... + +' + + + # TODO!!! Add user in sudo without password like "pi" = Run astroport under pi user IDEA. + sudo systemctl restart ipfs + + + fi + + +done < <(printf '%s\n' "$messages") + diff --git a/zen/tools/clean_OLD_ipfs.Qm.sh b/zen/tools/clean_OLD_ipfs.Qm.sh new file mode 100755 index 0000000..83b026d --- /dev/null +++ b/zen/tools/clean_OLD_ipfs.Qm.sh @@ -0,0 +1,7 @@ +#!/bin/bash +## In case OLD IPFSNODEID exists (remove it) +IPFSNODEID=$(ipfs id -f='\n') && [[ $IPFSNODEID == "" ]] && exit 1 +for DOTQm in ~/.zen/ipfs/.12D3KooW*; do + Qm=$(echo $DOTQm | cut -d '/' -f 6 | cut -d '.' -f 2) + [[ "$Qm" != "$IPFSNODEID" && "$Qm" != "" ]] && rm -Rf $DOTQm && echo "OLD IPFS ID REMOVED" +done diff --git a/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py b/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py new file mode 100755 index 0000000..167b691 --- /dev/null +++ b/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# This Python script gets /tmp/secret.dunikey produce with key_create_dunikey.py or from https://Cesium.app +# It create ED25519 ipfs (currently 0.7.0) Identity +######################################################################### +# sudo apt install protobuf-compiler +# pip3 install base58 google protobuf duniterpy +# wget https://github.com/libp2p/go-libp2p-core/raw/master/crypto/pb/crypto.proto +# protoc --python_out=. crypto.proto +######################################################################### + +import re, base58, base64, crypto_pb2 +import cryptography.hazmat.primitives.asymmetric.ed25519 as ed25519 +from cryptography.hazmat.primitives import serialization + +# TODO controls +# Capturing keys (from /tmp/secret.dunikey) + +dunikey = "/tmp/secret.dunikey" +for line in open(dunikey, "r"): + if re.search("pub", line): + shared_key = line.replace('\n','').split(': ')[1] + elif re.search("sec", line): + secure_key = line.replace('\n','').split(': ')[1] + +# Decoding keys +decoded_shared = base58.b58decode(shared_key) +decoded_secure = base58.b58decode(secure_key) +ipfs_shared = ed25519.Ed25519PublicKey.from_public_bytes(decoded_shared) +ipfs_secure = ed25519.Ed25519PrivateKey.from_private_bytes(decoded_secure[:32]) +ipfs_shared_bytes = ipfs_shared.public_bytes(encoding=serialization.Encoding.Raw, + format=serialization.PublicFormat.Raw) +ipfs_secure_bytes = ipfs_secure.private_bytes(encoding=serialization.Encoding.Raw, + format=serialization.PrivateFormat.Raw, + encryption_algorithm=serialization.NoEncryption()) + + +# Formulating PeerID +ipfs_pid = base58.b58encode(b'\x00$\x08\x01\x12 ' + ipfs_shared_bytes) +PeerID = ipfs_pid.decode('ascii') +print('PeerID={};'.format(ipfs_pid.decode('ascii'))) + + +# Serializing private key in IPFS-native mode, the private key contains public one +pkey = crypto_pb2.PrivateKey() +#pkey.Type = crypto_pb2.KeyType.Ed25519 +pkey.Type = 1 +pkey.Data = ipfs_secure_bytes + ipfs_shared_bytes +PrivKey = base64.b64encode(pkey.SerializeToString()).decode('ascii') +print('PrivKEY=' + base64.b64encode(pkey.SerializeToString()).decode('ascii')) + +# jq '.Identity.PeerID="$PeerID"' ~/.ipfs/config +# jq '.Identity.PrivKey="$PrivKey"' ~/.ipfs/config diff --git a/zen/tools/crypto.proto b/zen/tools/crypto.proto new file mode 100644 index 0000000..182b0d4 --- /dev/null +++ b/zen/tools/crypto.proto @@ -0,0 +1,22 @@ +syntax = "proto2"; + +package crypto.pb; + +option go_package = "github.com/libp2p/go-libp2p-core/crypto/pb"; + +enum KeyType { + RSA = 0; + Ed25519 = 1; + Secp256k1 = 2; + ECDSA = 3; +} + +message PublicKey { + required KeyType Type = 1; + required bytes Data = 2; +} + +message PrivateKey { + required KeyType Type = 1; + required bytes Data = 2; +} diff --git a/zen/tools/crypto_pb2.py b/zen/tools/crypto_pb2.py new file mode 100644 index 0000000..581ca18 --- /dev/null +++ b/zen/tools/crypto_pb2.py @@ -0,0 +1,163 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: crypto.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf import descriptor_pb2 +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='crypto.proto', + package='crypto.pb', + syntax='proto2', + serialized_pb=_b('\n\x0c\x63rypto.proto\x12\tcrypto.pb\";\n\tPublicKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c\"<\n\nPrivateKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c*9\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x0b\n\x07\x45\x64\x32\x35\x35\x31\x39\x10\x01\x12\r\n\tSecp256k1\x10\x02\x12\t\n\x05\x45\x43\x44SA\x10\x03\x42,Z*github.com/libp2p/go-libp2p-core/crypto/pb') +) +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +_KEYTYPE = _descriptor.EnumDescriptor( + name='KeyType', + full_name='crypto.pb.KeyType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='RSA', index=0, number=0, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='Ed25519', index=1, number=1, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='Secp256k1', index=2, number=2, + options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ECDSA', index=3, number=3, + options=None, + type=None), + ], + containing_type=None, + options=None, + serialized_start=150, + serialized_end=207, +) +_sym_db.RegisterEnumDescriptor(_KEYTYPE) + +KeyType = enum_type_wrapper.EnumTypeWrapper(_KEYTYPE) +RSA = 0 +Ed25519 = 1 +Secp256k1 = 2 +ECDSA = 3 + + + +_PUBLICKEY = _descriptor.Descriptor( + name='PublicKey', + full_name='crypto.pb.PublicKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Type', full_name='crypto.pb.PublicKey.Type', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='Data', full_name='crypto.pb.PublicKey.Data', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=27, + serialized_end=86, +) + + +_PRIVATEKEY = _descriptor.Descriptor( + name='PrivateKey', + full_name='crypto.pb.PrivateKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Type', full_name='crypto.pb.PrivateKey.Type', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + _descriptor.FieldDescriptor( + name='Data', full_name='crypto.pb.PrivateKey.Data', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + options=None), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=88, + serialized_end=148, +) + +_PUBLICKEY.fields_by_name['Type'].enum_type = _KEYTYPE +_PRIVATEKEY.fields_by_name['Type'].enum_type = _KEYTYPE +DESCRIPTOR.message_types_by_name['PublicKey'] = _PUBLICKEY +DESCRIPTOR.message_types_by_name['PrivateKey'] = _PRIVATEKEY +DESCRIPTOR.enum_types_by_name['KeyType'] = _KEYTYPE + +PublicKey = _reflection.GeneratedProtocolMessageType('PublicKey', (_message.Message,), dict( + DESCRIPTOR = _PUBLICKEY, + __module__ = 'crypto_pb2' + # @@protoc_insertion_point(class_scope:crypto.pb.PublicKey) + )) +_sym_db.RegisterMessage(PublicKey) + +PrivateKey = _reflection.GeneratedProtocolMessageType('PrivateKey', (_message.Message,), dict( + DESCRIPTOR = _PRIVATEKEY, + __module__ = 'crypto_pb2' + # @@protoc_insertion_point(class_scope:crypto.pb.PrivateKey) + )) +_sym_db.RegisterMessage(PrivateKey) + + +DESCRIPTOR.has_options = True +DESCRIPTOR._options = _descriptor._ParseOptions(descriptor_pb2.FileOptions(), _b('Z*github.com/libp2p/go-libp2p-core/crypto/pb')) +# @@protoc_insertion_point(module_scope) diff --git a/zen/tools/crypto_pb2.py.old b/zen/tools/crypto_pb2.py.old new file mode 100644 index 0000000..d6b953a --- /dev/null +++ b/zen/tools/crypto_pb2.py.old @@ -0,0 +1,162 @@ +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: crypto.proto + +import sys +_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1')) +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor.FileDescriptor( + name='crypto.proto', + package='crypto.pb', + syntax='proto2', + serialized_options=_b('Z*github.com/libp2p/go-libp2p-core/crypto/pb'), + serialized_pb=_b('\n\x0c\x63rypto.proto\x12\tcrypto.pb\";\n\tPublicKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c\"<\n\nPrivateKey\x12 \n\x04Type\x18\x01 \x02(\x0e\x32\x12.crypto.pb.KeyType\x12\x0c\n\x04\x44\x61ta\x18\x02 \x02(\x0c*9\n\x07KeyType\x12\x07\n\x03RSA\x10\x00\x12\x0b\n\x07\x45\x64\x32\x35\x35\x31\x39\x10\x01\x12\r\n\tSecp256k1\x10\x02\x12\t\n\x05\x45\x43\x44SA\x10\x03\x42,Z*github.com/libp2p/go-libp2p-core/crypto/pb') +) + +_KEYTYPE = _descriptor.EnumDescriptor( + name='KeyType', + full_name='crypto.pb.KeyType', + filename=None, + file=DESCRIPTOR, + values=[ + _descriptor.EnumValueDescriptor( + name='RSA', index=0, number=0, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='Ed25519', index=1, number=1, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='Secp256k1', index=2, number=2, + serialized_options=None, + type=None), + _descriptor.EnumValueDescriptor( + name='ECDSA', index=3, number=3, + serialized_options=None, + type=None), + ], + containing_type=None, + serialized_options=None, + serialized_start=150, + serialized_end=207, +) +_sym_db.RegisterEnumDescriptor(_KEYTYPE) + +KeyType = enum_type_wrapper.EnumTypeWrapper(_KEYTYPE) +RSA = 0 +Ed25519 = 1 +Secp256k1 = 2 +ECDSA = 3 + + + +_PUBLICKEY = _descriptor.Descriptor( + name='PublicKey', + full_name='crypto.pb.PublicKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Type', full_name='crypto.pb.PublicKey.Type', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Data', full_name='crypto.pb.PublicKey.Data', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=27, + serialized_end=86, +) + + +_PRIVATEKEY = _descriptor.Descriptor( + name='PrivateKey', + full_name='crypto.pb.PrivateKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + fields=[ + _descriptor.FieldDescriptor( + name='Type', full_name='crypto.pb.PrivateKey.Type', index=0, + number=1, type=14, cpp_type=8, label=2, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + _descriptor.FieldDescriptor( + name='Data', full_name='crypto.pb.PrivateKey.Data', index=1, + number=2, type=12, cpp_type=9, label=2, + has_default_value=False, default_value=_b(""), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + serialized_start=88, + serialized_end=148, +) + +_PUBLICKEY.fields_by_name['Type'].enum_type = _KEYTYPE +_PRIVATEKEY.fields_by_name['Type'].enum_type = _KEYTYPE +DESCRIPTOR.message_types_by_name['PublicKey'] = _PUBLICKEY +DESCRIPTOR.message_types_by_name['PrivateKey'] = _PRIVATEKEY +DESCRIPTOR.enum_types_by_name['KeyType'] = _KEYTYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +PublicKey = _reflection.GeneratedProtocolMessageType('PublicKey', (_message.Message,), dict( + DESCRIPTOR = _PUBLICKEY, + __module__ = 'crypto_pb2' + # @@protoc_insertion_point(class_scope:crypto.pb.PublicKey) + )) +_sym_db.RegisterMessage(PublicKey) + +PrivateKey = _reflection.GeneratedProtocolMessageType('PrivateKey', (_message.Message,), dict( + DESCRIPTOR = _PRIVATEKEY, + __module__ = 'crypto_pb2' + # @@protoc_insertion_point(class_scope:crypto.pb.PrivateKey) + )) +_sym_db.RegisterMessage(PrivateKey) + + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/zen/tools/diceware-wordlist.txt b/zen/tools/diceware-wordlist.txt new file mode 100644 index 0000000..31a2ae3 --- /dev/null +++ b/zen/tools/diceware-wordlist.txt @@ -0,0 +1,7776 @@ +11111 minou +11112 yes +11113 love +11114 kraken +11115 matiou +11116 cgeek +11121 g1 +11122 minette +11123 fred +11124 world +11125 bingo +11126 money +11131 duniter +11132 cesium +11133 greg +11134 linux +11135 linus +11136 peps +11141 tim +11142 emma +11143 kitty +11144 cyber +11145 tulipe +11146 moringa +11151 michel +11152 trm +11153 galuel +11154 gaston +11155 moul +11156 elois +11161 vanna +11162 papy +11163 mamy +11164 ludo +11165 fiston +11166 moon +11211 gilles +11212 demo +11213 alex +11214 0 +11215 1 +11216 10 +11221 100 +11222 1000 +11223 100eme +11224 101 +11225 10eme +11226 11 +11231 111 +11232 1111 +11233 11eme +11234 12 +11235 123 +11236 1234 +11241 12eme +11242 13 +11243 13eme +11244 14 +11245 1492 +11246 14eme +11251 15 +11252 1500 +11253 15eme +11254 16 +11255 1600 +11256 16eme +11261 17 +11262 1700 +11263 17eme +11264 18 +11265 1800 +11266 18eme +11311 19 +11312 1900 +11313 1910 +11314 1920 +11315 1925 +11316 1930 +11321 1935 +11322 1940 +11323 1945 +11324 1950 +11325 1955 +11326 1960 +11331 1965 +11332 1970 +11333 1975 +11334 1980 +11335 1985 +11336 1990 +11341 1991 +11342 1992 +11343 1993 +11344 1994 +11345 1995 +11346 1996 +11351 1997 +11352 1998 +11353 1999 +11354 19eme +11355 1er +11356 2 +11361 20 +11362 200 +11363 2000 +11364 2001 +11365 2002 +11366 2003 +11411 2004 +11412 2005 +11413 2006 +11414 2007 +11415 2008 +11416 2009 +11421 2010 +11422 2015 +11423 2020 +11424 2030 +11425 2035 +11426 2040 +11431 2045 +11432 2050 +11433 20eme +11434 21 +11435 21eme +11436 22 +11441 222 +11442 2222 +11443 22eme +11444 23 +11445 234 +11446 2345 +11451 23eme +11452 24 +11453 2468 +11454 24eme +11455 25 +11456 25eme +11461 26 +11462 26eme +11463 27 +11464 27eme +11465 28 +11466 28eme +11511 29 +11512 29eme +11513 2eme +11514 3 +11515 30 +11516 300 +11521 3000 +11522 30eme +11523 31 +11524 31eme +11525 32 +11526 32eme +11531 33 +11532 333 +11533 3333 +11534 33eme +11535 34 +11536 345 +11541 3456 +11542 34eme +11543 35 +11544 35eme +11545 36 +11546 36eme +11551 37 +11552 37eme +11553 38 +11554 38eme +11555 39 +11556 39eme +11561 3eme +11562 4 +11563 40 +11564 400 +11565 4000 +11566 40eme +11611 41 +11612 41eme +11613 42 +11614 42eme +11615 43 +11616 4321 +11621 43eme +11622 44 +11623 444 +11624 4444 +11625 44eme +11626 45 +11631 456 +11632 4567 +11633 45eme +11634 46 +11635 46eme +11636 47 +11641 47eme +11642 48 +11643 48eme +11644 49 +11645 49eme +11646 4eme +11651 5 +11652 50 +11653 500 +11654 5000 +11655 50eme +11656 51 +11661 51eme +11662 52 +11663 52eme +11664 53 +11665 53eme +11666 54 +12111 54eme +12112 55 +12113 555 +12114 5555 +12115 55eme +12116 56 +12121 567 +12122 5678 +12123 56eme +12124 57 +12125 57eme +12126 58 +12131 58eme +12132 59 +12133 59eme +12134 5eme +12135 6 +12136 60 +12141 600 +12142 6000 +12143 60eme +12144 61 +12145 61eme +12146 62 +12151 62eme +12152 63 +12153 63eme +12154 64 +12155 64eme +12156 65 +12161 65eme +12162 66 +12163 666 +12164 6666 +12165 66eme +12166 67 +12211 678 +12212 6789 +12213 67eme +12214 68 +12215 68eme +12216 69 +12221 69eme +12222 6eme +12223 7 +12224 70 +12225 700 +12226 7000 +12231 70eme +12232 71 +12233 71eme +12234 72 +12235 72eme +12236 73 +12241 73eme +12242 74 +12243 74eme +12244 75 +12245 75eme +12246 76 +12251 76eme +12252 77 +12253 777 +12254 7777 +12255 77eme +12256 78 +12261 789 +12262 78eme +12263 79 +12264 79eme +12265 7eme +12266 8 +12311 80 +12312 800 +12313 8000 +12314 80eme +12315 81 +12316 81eme +12321 82 +12322 82eme +12323 83 +12324 83eme +12325 84 +12326 84eme +12331 85 +12332 85eme +12333 86 +12334 86eme +12335 87 +12336 87eme +12341 88 +12342 888 +12343 8888 +12344 88eme +12345 89 +12346 89eme +12351 8eme +12352 9 +12353 90 +12354 900 +12355 9000 +12356 90eme +12361 91 +12362 91eme +12363 92 +12364 92eme +12365 93 +12366 93eme +12411 94 +12412 94eme +12413 95 +12414 95eme +12415 96 +12416 96eme +12421 97 +12422 97eme +12423 98 +12424 9876 +12425 98eme +12426 99 +12431 999 +12432 9999 +12433 99eme +12434 9eme +12435 a +12436 aa +12441 aaa +12442 aaaa +12443 ab +12444 abat +12445 abats +12446 abatte +12451 abc +12452 abject +12453 aboie +12454 abois +12455 aboli +12456 abonda +12461 abonne +12462 abord +12463 aborda +12464 aborde +12465 abords +12466 abouti +12511 aboya +12512 aboyer +12513 abri +12514 abris +12515 abrita +12516 abrite +12521 abroge +12522 abrupt +12523 abruti +12524 absent +12525 abus +12526 abusa +12531 abuse +12532 ac +12533 acajou +12534 accola +12535 accord +12536 accroc +12541 accru +12542 accule +12543 accusa +12544 ace +12545 acerbe +12546 aces +12551 achat +12552 acheta +12553 acheva +12554 acide +12555 acier +12556 acquis +12561 acquit +12562 acte +12563 actes +12564 acteur +12565 actif +12566 ad +12611 adage +12612 adapta +12613 adapte +12614 adepte +12615 adieu +12616 adjura +12621 adjure +12622 admet +12623 admira +12624 admis +12625 admise +12626 admit +12631 adonne +12632 adopta +12633 adopte +12634 adora +12635 adore +12636 adorer +12641 adores +12642 aduler +12643 adulte +12644 advenu +12645 ae +12646 aequo +12651 af +12652 affine +12653 affixe +12654 afflua +12655 afflue +12656 afflux +12661 affola +12662 affole +12663 affres +12664 afin +12665 ag +12666 agace +13111 agaces +13112 agacez +13113 agate +13114 agates +13115 agence +13116 agenda +13121 agent +13122 agi +13123 agile +13124 agio +13125 agios +13126 agiote +13131 agir +13132 agira +13133 agirai +13134 agis +13135 agisse +13136 agit +13141 agita +13142 agite +13143 agiter +13144 agonie +13145 agonir +13146 agrafe +13151 ah +13152 ahuri +13153 ahurir +13154 ai +13155 aida +13156 aidais +13161 aidant +13162 aide +13163 aider +13164 aides +13165 aidez +13166 aidiez +13211 aidons +13212 aie +13213 aient +13214 aies +13215 aigle +13216 aigles +13221 aigre +13222 aigres +13223 aigri +13224 aigrie +13225 aigris +13226 aigrit +13231 aigu +13232 aigus +13233 ail +13234 aile +13235 ailes +13236 aille +13241 aima +13242 aimant +13243 aime +13244 aimer +13245 aimera +13246 aimes +13251 aimez +13252 aine +13253 aines +13254 ainsi +13255 air +13256 aire +13261 airer +13262 aires +13263 airs +13264 aise +13265 aises +13266 ait +13311 aj +13312 ajonc +13313 ajout +13314 ajouta +13315 ajuste +13316 ak +13321 al +13322 alarme +13323 album +13324 alcool +13325 alerta +13326 alevin +13331 alezan +13332 alfa +13333 alfas +13334 algue +13335 algues +13336 alias +13341 alibi +13342 aliter +13343 alla +13344 aller +13345 allez +13346 allia +13351 allie +13352 allier +13353 allies +13354 alloue +13355 allume +13356 allure +13361 aloi +13362 alors +13363 alpaga +13364 alpage +13365 alpin +13366 alpins +13411 alter +13412 alto +13413 altos +13414 alu +13415 aluner +13416 am +13421 amant +13422 amants +13423 amarre +13424 amas +13425 amble +13426 ambler +13431 ambre +13432 ambrer +13433 amen +13434 amena +13435 amenda +13436 amener +13441 amer +13442 amers +13443 ameuta +13444 ami +13445 amibe +13446 amical +13451 amie +13452 amies +13453 amis +13454 amont +13455 amour +13456 amours +13461 amphi +13462 ample +13463 amples +13464 ampli +13465 amputa +13466 amurer +13511 amusa +13512 amuse +13513 amuser +13514 amusez +13515 an +13516 anche +13521 anches +13522 ancra +13523 ancre +13524 ancrer +13525 ancres +13526 ancrez +13531 and +13532 ange +13533 anges +13534 angine +13535 angle +13536 angles +13541 angora +13542 anima +13543 anime +13544 animez +13545 anis +13546 aniser +13551 anneau +13552 annexa +13553 annota +13554 annote +13555 anode +13556 anodes +13561 ans +13562 anse +13563 anses +13564 antan +13565 antre +13566 antres +13611 anus +13612 ao +13613 aorte +13614 ap +13615 aphte +13616 aplani +13621 apparu +13622 appas +13623 appel +13624 appels +13625 apport +13626 apposa +13631 appose +13632 appris +13633 appui +13634 appuie +13635 appuis +13636 apte +13641 aptes +13642 apura +13643 apure +13644 apurer +13645 apures +13646 aq +13651 ar +13652 arabe +13653 araser +13654 arbora +13655 arbre +13656 arc +13661 arcade +13662 arche +13663 archet +13664 arcs +13665 ardu +13666 ardue +14111 ardues +14112 ardus +14113 are +14114 ares +14115 argile +14116 argot +14121 argots +14122 aria +14123 arias +14124 aride +14125 arides +14126 arma +14131 armais +14132 armait +14133 armant +14134 arme +14135 arment +14136 armer +14141 armes +14142 armez +14143 arpent +14144 arrime +14145 arriva +14146 arrive +14151 arroge +14152 arrosa +14153 art +14154 arts +14155 as +14156 asile +14161 aspect +14162 aspic +14163 assagi +14164 assez +14165 assis +14166 assit +14211 assoie +14212 assume +14213 astral +14214 astre +14215 astres +14216 astuce +14221 at +14222 atlas +14223 atoll +14224 atome +14225 atomes +14226 atone +14231 atones +14232 atours +14233 atout +14234 atouts +14235 attire +14236 au +14241 aube +14242 aubier +14243 aucun +14244 audit +14245 audits +14246 auge +14251 auges +14252 augura +14253 aune +14254 aunes +14255 auquel +14256 aura +14261 aurai +14262 auras +14263 aurez +14264 auriez +14265 aussi +14266 autel +14311 auteur +14312 auto +14313 autos +14314 autre +14315 auvent +14316 aux +14321 av +14322 avais +14323 avait +14324 aval +14325 avala +14326 avale +14331 avales +14332 avalez +14333 avals +14334 avance +14335 avant +14336 avants +14341 avare +14342 avarie +14343 avatar +14344 avec +14345 avenir +14346 avenu +14351 avenue +14352 avenus +14353 averse +14354 aveu +14355 aveuli +14356 aveux +14361 avez +14362 avide +14363 avides +14364 aviez +14365 avili +14366 avilir +14411 avilis +14412 avilit +14413 avion +14414 avions +14415 avis +14416 avisa +14421 avise +14422 avises +14423 aviso +14424 avisos +14425 aviva +14426 avive +14431 avives +14432 avoir +14433 avoirs +14434 avons +14435 avoua +14436 avoue +14441 avouer +14442 avouez +14443 avril +14444 aw +14445 ax +14446 axa +14451 axais +14452 axait +14453 axant +14454 axe +14455 axent +14456 axer +14461 axera +14462 axes +14463 axez +14464 axiez +14465 axiome +14466 axons +14511 ay +14512 ayant +14513 ayez +14514 ayons +14515 az +14516 azote +14521 azur +14522 azyme +14523 b +14524 ba +14525 baba +14526 babas +14531 babil +14532 bac +14533 bacon +14534 bacs +14535 badaud +14536 badge +14541 badges +14542 badin +14543 badine +14544 bafoua +14545 bagne +14546 bagou +14551 bague +14552 baguer +14553 bahut +14554 bahuts +14555 bai +14556 baie +14561 baies +14562 baigna +14563 bail +14564 bain +14565 bains +14566 bais +14611 baiser +14612 baissa +14613 baisse +14614 bal +14615 balade +14616 balai +14621 balais +14622 balaya +14623 balisa +14624 balise +14625 balle +14626 baller +14631 balles +14632 bals +14633 balsa +14634 bambin +14635 bambou +14636 ban +14641 banal +14642 banals +14643 banane +14644 banc +14645 bancal +14646 banco +14651 bancos +14652 bancs +14653 bande +14654 bandes +14655 bang +14656 bangs +14661 banjo +14662 banni +14663 bannit +14664 banque +14665 bans +14666 bantou +15111 baquet +15112 bar +15113 barbe +15114 barbet +15115 barbu +15116 barda +15121 bardas +15122 barde +15123 bardes +15124 barge +15125 baril +15126 barman +15131 baron +15132 barra +15133 barre +15134 barrez +15135 barrir +15136 bars +15141 bas +15142 basa +15143 basait +15144 basane +15145 base +15146 basent +15151 baser +15152 bases +15153 basez +15154 basque +15155 basse +15156 bassin +15161 basson +15162 bat +15163 bateau +15164 bats +15165 batte +15166 battez +15211 battit +15212 battre +15213 battu +15214 battus +15215 baudet +15216 bauge +15221 baume +15222 baux +15223 bava +15224 bavait +15225 bave +15226 baver +15231 baves +15232 bavez +15233 baviez +15234 bayer +15235 bazar +15236 bb +15241 bbb +15242 bbbb +15243 bc +15244 bcd +15245 bd +15246 be +15251 beau +15252 beaux +15253 bec +15254 becs +15255 becter +15256 beige +15261 beiges +15262 bel +15263 belge +15264 belle +15265 belles +15266 belon +15311 belons +15312 benne +15313 bennes +15314 berce +15315 bercer +15316 bercez +15321 berge +15322 berger +15323 berlue +15324 berna +15325 berne +15326 bernez +15331 besoin +15332 bette +15333 bettes +15334 bf +15335 bg +15336 bh +15341 bi +15342 biais +15343 biaisa +15344 bible +15345 biceps +15346 biche +15351 bicher +15352 bidet +15353 bidon +15354 bien +15355 biens +15356 biffa +15361 biffe +15362 bigler +15363 bijou +15364 bilan +15365 bilans +15366 bile +15411 biler +15412 bille +15413 biller +15414 billes +15415 billet +15416 billot +15421 binage +15422 biner +15423 bis +15424 bise +15425 biseau +15426 biser +15431 bises +15432 bison +15433 bisons +15434 bistro +15435 bit +15436 bits +15441 bitume +15442 bj +15443 bk +15444 bl +15445 blague +15446 blanc +15451 blancs +15452 blaser +15453 blesse +15454 blet +15455 blets +15456 bleu +15461 bleue +15462 bleui +15463 bleuie +15464 bleuir +15465 bleus +15466 blinde +15511 bloc +15512 blocs +15513 blond +15514 bloque +15515 blouse +15516 blues +15521 bluff +15522 bm +15523 bn +15524 bo +15525 boa +15526 boas +15531 bob +15532 bobine +15533 bobs +15534 bocal +15535 bocaux +15536 bock +15541 bocks +15542 boeuf +15543 bogue +15544 boira +15545 boirai +15546 boire +15551 boirez +15552 bois +15553 boit +15554 boita +15555 boite +15556 boitez +15561 boive +15562 boives +15563 bol +15564 bolet +15565 bolide +15566 bols +15611 bombe +15612 bomber +15613 bombes +15614 bon +15615 bonbon +15616 bond +15621 bonde +15622 bondes +15623 bondi +15624 bondir +15625 bondit +15626 bonds +15631 boni +15632 bonis +15633 bonne +15634 bonnes +15635 bons +15636 bonus +15641 bonze +15642 bonzes +15643 boom +15644 boots +15645 bord +15646 borda +15651 borde +15652 border +15653 bordes +15654 bordez +15655 bords +15656 borgne +15661 borna +15662 borne +15663 borner +15664 bornez +15665 bosse +15666 bosser +16111 bosses +16112 bossez +16113 bossu +16114 bot +16115 bots +16116 botte +16121 bouc +16122 boucs +16123 bouda +16124 boude +16125 bouder +16126 boudes +16131 boudez +16132 boudin +16133 boue +16134 boues +16135 bouffe +16136 bouffi +16141 bouge +16142 bouges +16143 bougre +16144 boule +16145 boulon +16146 boumer +16151 bourg +16152 bourre +16153 bous +16154 bouse +16155 bout +16156 bouter +16161 bouts +16162 bovin +16163 bovins +16164 box +16165 boxa +16166 boxe +16211 boxer +16212 boxera +16213 boxers +16214 boxes +16215 boxeur +16216 boxez +16221 boxiez +16222 boxons +16223 boy +16224 boyau +16225 boys +16226 bp +16231 bq +16232 br +16233 brada +16234 brade +16235 brader +16236 brades +16241 bradez +16242 braire +16243 braise +16244 brame +16245 bramer +16246 branle +16251 braque +16252 bras +16253 brassa +16254 brava +16255 brave +16256 braver +16261 braves +16262 bravez +16263 bravo +16264 bravos +16265 break +16266 breaks +16311 brebis +16312 bref +16313 brefs +16314 brevet +16315 bric +16316 brick +16321 bricks +16322 brida +16323 bride +16324 bridez +16325 bridge +16326 brie +16331 brilla +16332 brille +16333 brima +16334 brime +16335 brimer +16336 brimes +16341 brin +16342 brins +16343 brio +16344 brique +16345 bris +16346 brisa +16351 brise +16352 brises +16353 broc +16354 brocs +16355 broda +16356 brode +16361 broie +16362 bronza +16363 bronze +16364 brou +16365 brouta +16366 broya +16411 bru +16412 bruine +16413 bruir +16414 bruire +16415 bruit +16416 bruits +16421 brume +16422 brumer +16423 brun +16424 brune +16425 bruni +16426 brunie +16431 brunir +16432 brunit +16433 bruns +16434 brus +16435 brut +16436 brute +16441 bruts +16442 bs +16443 bt +16444 bu +16445 buccal +16446 budget +16451 bue +16452 bues +16453 buffle +16454 buggy +16455 buggys +16456 buis +16461 bulbe +16462 bulle +16463 bulles +16464 bure +16465 burent +16466 burin +16511 bus +16512 busard +16513 buse +16514 buses +16515 buste +16516 but +16521 buta +16522 butait +16523 butane +16524 butant +16525 bute +16526 buter +16531 butera +16532 butes +16533 buteur +16534 butez +16535 butiez +16536 butin +16541 butins +16542 butor +16543 butors +16544 buts +16545 butte +16546 buvait +16551 buvard +16552 buveur +16553 buvez +16554 bv +16555 bw +16556 bx +16561 by +16562 bz +16563 c +16564 ca +16565 caban +16566 cabas +16611 cabine +16612 cabra +16613 cabre +16614 cabres +16615 cabrez +16616 cabri +16621 cacao +16622 cacha +16623 cache +16624 cacher +16625 caches +16626 cachet +16631 cachou +16632 cactus +16633 caddie +16634 cade +16635 cades +16636 cadet +16641 cadi +16642 cadis +16643 cadra +16644 cadre +16645 cadrer +16646 cadrez +16651 caduc +16652 cafard +16653 cage +16654 cages +16655 cagibi +16656 cahot +16661 cahota +16662 cahote +16663 cahots +16664 cajou +16665 cajous +16666 cajun +21111 cake +21112 cakes +21113 cal +21114 cala +21115 calage +21116 calais +21121 calant +21122 calcul +21123 cale +21124 calent +21125 caler +21126 cales +21131 calez +21132 calma +21133 calmar +21134 calme +21135 calot +21136 cals +21141 calva +21142 came +21143 camer +21144 cames +21145 camion +21146 camp +21151 campa +21152 campe +21153 camper +21154 campes +21155 campez +21156 camps +21161 camus +21162 canal +21163 canard +21164 canari +21165 cancer +21166 candi +21211 candir +21212 cane +21213 caner +21214 canes +21215 canif +21216 canna +21221 canne +21222 canon +21223 canot +21224 canots +21225 canule +21226 cap +21231 cape +21232 caper +21233 capes +21234 capot +21235 capots +21236 caps +21241 capta +21242 capte +21243 capter +21244 captes +21245 captez +21246 captif +21251 caquer +21252 car +21253 carafe +21254 carat +21255 carcan +21256 carde +21261 cargo +21262 cargos +21263 caria +21264 carie +21265 caries +21266 carmin +21311 carnet +21312 carpe +21313 carre +21314 carrer +21315 carres +21316 cars +21321 carte +21322 carter +21323 cartes +21324 carton +21325 cas +21326 casa +21331 casait +21332 casant +21333 case +21334 casent +21335 caser +21336 casera +21341 cases +21342 casez +21343 casino +21344 cassa +21345 casse +21346 casser +21351 cassez +21352 cassis +21353 caste +21354 catch +21355 catir +21356 caudal +21361 causa +21362 cause +21363 causer +21364 causes +21365 cavale +21366 cave +21411 caveau +21412 caver +21413 caves +21414 caviar +21415 cb +21416 cc +21421 ccc +21422 cccc +21423 cd +21424 cde +21425 ce +21426 ceci +21431 cedex +21432 ceint +21433 ceints +21434 cela +21435 celer +21436 celle +21441 celui +21442 cens +21443 cent +21444 centra +21445 cents +21446 cep +21451 ceps +21452 cercla +21453 cerf +21454 cerfs +21455 cerna +21456 cerne +21461 cerner +21462 cernez +21463 ces +21464 cessa +21465 cesse +21466 cesser +21511 cet +21512 cette +21513 ceux +21514 cf +21515 cg +21516 ch +21521 chacun +21522 chah +21523 chahs +21524 chai +21525 chair +21526 chaire +21531 chais +21532 champ +21533 chance +21534 change +21535 chant +21536 chanta +21541 chaos +21542 chape +21543 char +21544 charma +21545 chars +21546 charte +21551 chas +21552 chat +21553 chaton +21554 chats +21555 chaud +21556 chauds +21561 chaume +21562 chauve +21563 chaux +21564 check +21565 chef +21566 chefs +21611 chelem +21612 chemin +21613 chenet +21614 chenil +21615 cher +21616 chers +21621 chevet +21622 chez +21623 chic +21624 chien +21625 chiens +21626 chier +21631 chiot +21632 chiper +21633 chips +21634 chique +21635 chlore +21636 choc +21641 chocs +21642 choeur +21643 choie +21644 choies +21645 choir +21646 choisi +21651 choix +21652 chope +21653 chopes +21654 choqua +21655 choque +21656 choral +21661 chose +21662 choses +21663 chou +21664 choux +21665 choya +21666 choyer +22111 chrome +22112 chromo +22113 chrono +22114 chut +22115 chuta +22116 chute +22121 chutes +22122 chutez +22123 ci +22124 cible +22125 cibler +22126 cibles +22131 cidre +22132 cidres +22133 ciel +22134 cierge +22135 cieux +22136 cil +22141 ciller +22142 cils +22143 cime +22144 cimes +22145 cinq +22146 cintra +22151 cintre +22152 cira +22153 cirais +22154 cirant +22155 cire +22156 cirent +22161 cirer +22162 cirera +22163 cires +22164 cireur +22165 cirez +22166 cirrus +22211 cita +22212 cite +22213 citer +22214 cites +22215 citez +22216 citron +22221 civet +22222 civets +22223 civil +22224 cj +22225 ck +22226 cl +22231 claie +22232 clair +22233 clairs +22234 clam +22235 clama +22236 clame +22241 clamer +22242 clames +22243 clamez +22244 clams +22245 clan +22246 clans +22251 clapir +22252 claque +22253 classe +22254 claver +22255 clef +22256 clefs +22261 clerc +22262 clercs +22263 climat +22264 clin +22265 clins +22266 clip +22311 clips +22312 clique +22313 cloche +22314 clone +22315 clora +22316 cloras +22321 clore +22322 clos +22323 close +22324 clou +22325 cloua +22326 cloue +22331 cloues +22332 clous +22333 clown +22334 club +22335 clubs +22336 cm +22341 cn +22342 co +22343 cobol +22344 cobra +22345 cocha +22346 coche +22351 cochez +22352 cochon +22353 coco +22354 cocon +22355 cocons +22356 coda +22361 codage +22362 codais +22363 codait +22364 codas +22365 code +22366 codent +22411 coder +22412 codera +22413 codes +22414 codeur +22415 codez +22416 codons +22421 coeur +22422 coeurs +22423 cogna +22424 cognac +22425 cogne +22426 cogner +22431 cognez +22432 cohue +22433 coi +22434 coiffe +22435 coin +22436 coince +22441 coing +22442 coings +22443 coins +22444 cois +22445 coite +22446 coites +22451 coke +22452 col +22453 cola +22454 colin +22455 colis +22456 colite +22461 colla +22462 colle +22463 colles +22464 collez +22465 colon +22466 colore +22511 cols +22512 colt +22513 colts +22514 colza +22515 coma +22516 comas +22521 combat +22522 combla +22523 comme +22524 commet +22525 compta +22526 compte +22531 comte +22532 comtes +22533 con +22534 conclu +22535 confia +22536 confus +22541 conne +22542 connes +22543 connu +22544 connue +22545 connut +22546 conque +22551 cons +22552 conte +22553 contes +22554 contre +22555 convie +22556 convoi +22561 copia +22562 copie +22563 copier +22564 copies +22565 copine +22566 coq +22611 coqs +22612 coque +22613 coquet +22614 cor +22615 corail +22616 corde +22621 corder +22622 corne +22623 corner +22624 cornet +22625 cornu +22626 cornus +22631 coron +22632 corps +22633 cors +22634 corsa +22635 corse +22636 corses +22641 corset +22642 corsez +22643 cortex +22644 coryza +22645 cosmos +22646 cosse +22651 cossu +22652 cossue +22653 cossus +22654 cosy +22655 cosys +22656 cota +22661 cotais +22662 cote +22663 coteau +22664 cotent +22665 coter +22666 cotes +23111 cotez +23112 cotir +23113 coton +23114 cotons +23115 cotte +23116 cou +23121 couac +23122 couche +23123 coucou +23124 coud +23125 coude +23126 couder +23131 couds +23132 coula +23133 coule +23134 coulez +23135 coup +23136 coupa +23141 coupe +23142 coupez +23143 coupla +23144 coups +23145 cour +23146 courbe +23151 coure +23152 coures +23153 courre +23154 cours +23155 course +23156 court +23161 couru +23162 courue +23163 courus +23164 cous +23165 couse +23166 couses +23211 cousez +23212 cousu +23213 cousus +23214 coutil +23215 couva +23216 couve +23221 couvez +23222 couvre +23223 cp +23224 cpt +23225 cq +23226 cr +23231 crabe +23232 crack +23233 cracks +23234 craie +23235 craies +23236 crains +23241 crampe +23242 cran +23243 crans +23244 crasse +23245 crawl +23246 crayon +23251 credo +23252 creuse +23253 creux +23254 creva +23255 crevez +23256 cri +23261 cria +23262 criais +23263 criait +23264 criard +23265 crible +23266 cric +23311 cricri +23312 crics +23313 crie +23314 crier +23315 criera +23316 cries +23321 crieur +23322 criez +23323 crime +23324 crimes +23325 crin +23326 crins +23331 crions +23332 cris +23333 crise +23334 crissa +23335 croc +23336 crocs +23341 croie +23342 croira +23343 crois +23344 croisa +23345 croise +23346 croit +23351 croix +23352 croqua +23353 croque +23354 cross +23355 crosse +23356 crotte +23361 croula +23362 cru +23363 crue +23364 cruel +23365 cruels +23366 crues +23411 crus +23412 crut +23413 cs +23414 ct +23415 cu +23416 cuba +23421 cubait +23422 cubant +23423 cube +23424 cuber +23425 cubera +23426 cubes +23431 cuir +23432 cuira +23433 cuirai +23434 cuire +23435 cuirs +23436 cuis +23441 cuise +23442 cuises +23443 cuisez +23444 cuisse +23445 cuit +23446 cuite +23451 cuiter +23452 cuits +23453 cuivre +23454 cul +23455 culer +23456 culot +23461 culs +23462 culte +23463 cultes +23464 cumin +23465 cumul +23466 cumula +23511 cupide +23512 cupule +23513 curage +23514 cure +23515 curer +23516 cures +23521 curry +23522 cuti +23523 cuva +23524 cuvait +23525 cuve +23526 cuver +23531 cuves +23532 cuvez +23533 cuviez +23534 cv +23535 cw +23536 cx +23541 cy +23542 cycle +23543 cycles +23544 cygne +23545 cygnes +23546 cz +23551 d +23552 da +23553 dada +23554 dadas +23555 dague +23556 dagues +23561 daigne +23562 daim +23563 daims +23564 dais +23565 dalla +23566 dalle +23611 dame +23612 damer +23613 dames +23614 damna +23615 damne +23616 damnez +23621 dan +23622 dandy +23623 dandys +23624 dans +23625 dansa +23626 danse +23631 danser +23632 dansez +23633 darce +23634 darces +23635 dard +23636 darder +23641 dards +23642 darne +23643 darnes +23644 darse +23645 dartre +23646 data +23651 datait +23652 datant +23653 date +23654 dater +23655 dates +23656 dateur +23661 datez +23662 datif +23663 dation +23664 datte +23665 dattes +23666 daube +24111 dauber +24112 db +24113 dc +24114 dd +24115 ddd +24116 dddd +24121 de +24122 debout +24123 dedans +24124 def +24125 delta +24126 demain +24131 demi +24132 demie +24133 demis +24134 denier +24135 dense +24136 denses +24141 dent +24142 dents +24143 derby +24144 derbys +24145 derme +24146 des +24151 design +24152 dessus +24153 destin +24154 dette +24155 deuil +24156 deuils +24161 deux +24162 devais +24163 devait +24164 devant +24165 devenu +24166 devez +24211 devin +24212 devina +24213 devins +24214 devint +24215 devis +24216 devons +24221 devra +24222 devras +24223 devrez +24224 df +24225 dg +24226 dh +24231 di +24232 diapo +24233 dicta +24234 dicte +24235 dictez +24236 dicton +24241 diesel +24242 dieu +24243 dieux +24244 diffus +24245 digit +24246 digits +24251 digne +24252 digue +24253 dilua +24254 dilue +24255 dinde +24256 dindon +24261 dinghy +24262 dingo +24263 dingos +24264 diode +24265 dira +24266 dirai +24311 dirait +24312 diras +24313 dire +24314 direct +24315 dires +24316 direz +24321 dirige +24322 dirons +24323 dis +24324 discal +24325 disco +24326 dise +24331 disert +24332 dises +24333 disiez +24334 dispos +24335 dit +24336 dite +24341 dites +24342 dito +24343 dits +24344 divan +24345 divin +24346 divisa +24351 divise +24352 dix +24353 dj +24354 dk +24355 dl +24356 dm +24361 dn +24362 do +24363 docile +24364 dock +24365 docks +24366 docte +24411 doctes +24412 dodu +24413 dodue +24414 dodues +24415 dodus +24416 dogme +24421 dogmes +24422 dogue +24423 dogues +24424 doigt +24425 doigts +24426 dois +24431 doit +24432 doive +24433 doives +24434 dollar +24435 dolmen +24436 domina +24441 domine +24442 dompta +24443 don +24444 donc +24445 donjon +24446 donna +24451 donne +24452 donnes +24453 donnez +24454 dons +24455 dont +24456 dopa +24461 dopait +24462 dopant +24463 dope +24464 doper +24465 dopes +24466 dopez +24511 dopons +24512 dora +24513 dorade +24514 dorait +24515 dorant +24516 dore +24521 dorent +24522 dorer +24523 doreur +24524 dorme +24525 dormi +24526 dormir +24531 dors +24532 dort +24533 dorure +24534 dos +24535 dosa +24536 dosage +24541 dosais +24542 dosant +24543 dose +24544 dosent +24545 doser +24546 doses +24551 dosez +24552 dosons +24553 dot +24554 dota +24555 dote +24556 dotent +24561 doter +24562 dotes +24563 dotez +24564 dotons +24565 dots +24566 douane +24611 douant +24612 doubla +24613 double +24614 douce +24615 douces +24616 douche +24621 douer +24622 douta +24623 doute +24624 doutez +24625 douve +24626 douves +24631 doux +24632 douze +24633 doyen +24634 dp +24635 dq +24636 dr +24641 dragon +24642 drain +24643 draine +24644 drame +24645 drap +24646 drapa +24651 drape +24652 drapes +24653 drapez +24654 draps +24655 dressa +24656 dresse +24661 drille +24662 drisse +24663 driver +24664 drogua +24665 droit +24666 drop +25111 drops +25112 dru +25113 drue +25114 drues +25115 drus +25116 ds +25121 dt +25122 du +25123 duc +25124 ducal +25125 ducale +25126 ducs +25131 due +25132 duel +25133 duels +25134 dues +25135 dune +25136 dunes +25141 duo +25142 duos +25143 dupa +25144 dupais +25145 dupait +25146 dupant +25151 dupe +25152 duper +25153 dupera +25154 dupes +25155 dupez +25156 dupons +25161 dur +25162 dura +25163 durait +25164 durci +25165 durcit +25166 dure +25211 durent +25212 durer +25213 durera +25214 dures +25215 durs +25216 dus +25221 dut +25222 duvet +25223 duvets +25224 dv +25225 dw +25226 dx +25231 dy +25232 dynamo +25233 dz +25234 e +25235 ea +25236 eau +25241 eaux +25242 eb +25243 ec +25244 ed +25245 ee +25246 eee +25251 eeee +25252 ef +25253 efface +25254 effara +25255 effare +25256 effet +25261 effets +25262 effila +25263 effile +25264 effort +25265 effroi +25266 efg +25311 eg +25312 ego +25313 eh +25314 ei +25315 eider +25316 ej +25321 ek +25322 el +25323 elfe +25324 elfes +25325 elle +25326 elles +25331 em +25332 embuer +25333 emmena +25334 empan +25335 empara +25336 empli +25341 emplis +25342 emplit +25343 empois +25344 en +25345 encan +25346 encas +25351 encore +25352 encra +25353 encre +25354 encres +25355 encrez +25356 endos +25361 enduis +25362 endure +25363 enfer +25364 enfila +25365 enfile +25366 enfin +25411 enfla +25412 enfle +25413 enfler +25414 enflez +25415 enfoui +25416 enfui +25421 enfuit +25422 enfuma +25423 engin +25424 enivre +25425 enjeu +25426 enjeux +25431 enlisa +25432 ennemi +25433 ennui +25434 ennuie +25435 ennuis +25436 ennuya +25441 enquit +25442 enraya +25443 enraye +25444 enroba +25445 ensuit +25446 entama +25451 entame +25452 enter +25453 entier +25454 entra +25455 entre +25456 entrez +25461 envia +25462 envie +25463 envier +25464 envies +25465 enviez +25466 envoi +25511 envois +25512 envol +25513 envola +25514 envols +25515 enzyme +25516 eo +25521 ep +25522 eq +25523 er +25524 erg +25525 ergot +25526 ergots +25531 ergs +25532 ermite +25533 erra +25534 erre +25535 errer +25536 errera +25541 erres +25542 errez +25543 erriez +25544 ersatz +25545 es +25546 escale +25551 esche +25552 escher +25553 esches +25554 espion +25555 espoir +25556 essai +25561 essaim +25562 essaya +25563 esse +25564 esses +25565 essor +25566 essore +25611 essuya +25612 est +25613 ester +25614 estima +25615 estoc +25616 et +25621 ethnie +25622 eu +25623 eue +25624 eues +25625 euh +25626 eurent +25631 euro +25632 euros +25633 eus +25634 eut +25635 eux +25636 ev +25641 ew +25642 ex +25643 exact +25644 exacte +25645 exacts +25646 exalte +25651 exauce +25652 excite +25653 exclu +25654 excusa +25655 excuse +25656 exerce +25661 exhala +25662 exige +25663 exiger +25664 exiges +25665 exigu +25666 exil +26111 exila +26112 exile +26113 exiles +26114 exils +26115 exista +26116 exocet +26121 exode +26122 expert +26123 expia +26124 expie +26125 expier +26126 expira +26131 expire +26132 exposa +26133 expose +26134 extra +26135 exulte +26136 ey +26141 ez +26142 f +26143 fa +26144 fable +26145 fabula +26146 face +26151 faces +26152 facial +26153 facto +26154 factor +26155 fade +26156 fades +26161 fadeur +26162 fagot +26163 faible +26164 faibli +26165 faim +26166 faire +26211 fais +26212 fait +26213 faite +26214 faites +26215 faits +26216 fakir +26221 fallu +26222 falot +26223 falote +26224 falots +26225 fameux +26226 fan +26231 fana +26232 fanal +26233 fane +26234 fanent +26235 faner +26236 fanes +26241 fange +26242 fanion +26243 fanon +26244 fanons +26245 fans +26246 faon +26251 faons +26252 far +26253 faraud +26254 farce +26255 farces +26256 farci +26261 farcir +26262 fard +26263 farda +26264 farde +26265 fardes +26266 fardez +26311 fards +26312 farine +26313 fars +26314 fart +26315 farts +26316 fasse +26321 fasses +26322 faste +26323 fat +26324 fatal +26325 fats +26326 fauche +26331 faudra +26332 faune +26333 faut +26334 faute +26335 fautes +26336 fauve +26341 faux +26342 fax +26343 faxer +26344 fb +26345 fc +26346 fco +26351 fd +26352 fe +26353 feins +26354 feint +26355 feinte +26356 feints +26361 femme +26362 fend +26363 fende +26364 fendes +26365 fendra +26366 fendre +26411 fends +26412 fendu +26413 fente +26414 fer +26415 fera +26416 ferai +26421 ferais +26422 feras +26423 ferez +26424 feriez +26425 ferma +26426 ferme +26431 fermer +26432 fermez +26433 feront +26434 ferrer +26435 fers +26436 fesse +26441 fesser +26442 festif +26443 feu +26444 feue +26445 feues +26446 feutra +26451 feux +26452 fez +26453 ff +26454 fff +26455 ffff +26456 fg +26461 fgh +26462 fh +26463 fi +26464 fia +26465 fiacre +26466 fiais +26511 fiait +26512 fiance +26513 fiant +26514 fiasco +26515 fibre +26516 fibres +26521 fibule +26522 ficela +26523 ficha +26524 fiche +26525 fichez +26526 fichu +26531 fictif +26532 fie +26533 fief +26534 fiefs +26535 fiel +26536 fient +26541 fier +26542 fiera +26543 fierai +26544 fieras +26545 fiers +26546 fies +26551 fiez +26552 fifre +26553 fige +26554 figea +26555 figer +26556 figes +26561 figez +26562 figue +26563 figura +26564 fiiez +26565 fiions +26566 fil +26611 fila +26612 filait +26613 filant +26614 file +26615 filent +26616 filer +26621 filera +26622 files +26623 filet +26624 filets +26625 filez +26626 filin +26631 fille +26632 filles +26633 film +26634 filma +26635 filme +26636 filmes +26641 films +26642 filon +26643 filons +26644 filou +26645 fils +26646 filtra +26651 filtre +26652 fin +26653 final +26654 finals +26655 finaud +26656 fine +26661 fines +26662 fini +26663 finie +26664 finies +26665 finir +26666 finira +31111 finis +31112 finit +31113 fins +31114 fiole +31115 fioles +31116 fions +31121 firent +31122 firme +31123 fisc +31124 fiscal +31125 fiscs +31126 fit +31131 fixa +31132 fixais +31133 fixait +31134 fixant +31135 fixe +31136 fixer +31141 fixes +31142 fixez +31143 fj +31144 fjord +31145 fjords +31146 fk +31151 fl +31152 flacon +31153 flair +31154 flairs +31155 flambe +31156 flamme +31161 flan +31162 flanc +31163 flans +31164 flapi +31165 flash +31166 flatta +31211 flatte +31212 flegme +31213 flemme +31214 fleur +31215 fleura +31216 fleuri +31221 flippe +31222 flirt +31223 flirts +31224 flood +31225 flore +31226 flores +31231 flot +31232 flots +31233 flou +31234 floua +31235 floue +31236 flous +31241 fluer +31242 fluet +31243 fluide +31244 fluor +31245 fluors +31246 flux +31251 fm +31252 fn +31253 fo +31254 foc +31255 focal +31256 focale +31261 focs +31262 focus +31263 foehn +31264 foetus +31265 foi +31266 foie +31311 foies +31312 foin +31313 foins +31314 foire +31315 fois +31316 foison +31321 fol +31322 folie +31323 folies +31324 folio +31325 folios +31326 foliot +31331 folle +31332 fonce +31333 foncer +31334 fonces +31335 foncez +31336 fond +31341 fonda +31342 fonde +31343 fonder +31344 fondez +31345 fonds +31346 fondu +31351 fondue +31352 fondus +31353 font +31354 fonte +31355 fontes +31356 fonts +31361 foot +31362 for +31363 fora +31364 forage +31365 forain +31366 forais +31411 forait +31412 force +31413 forces +31414 fore +31415 forent +31416 forer +31421 forera +31422 fores +31423 foret +31424 forez +31425 forge +31426 forger +31431 forges +31432 forgez +31433 foriez +31434 forma +31435 forme +31436 formel +31441 former +31442 formes +31443 formez +31444 formol +31445 fors +31446 fort +31451 forte +31452 forts +31453 forum +31454 forums +31455 fosse +31456 fosses +31461 fou +31462 fouet +31463 fouine +31464 fouir +31465 foula +31466 foule +31511 foules +31512 four +31513 fours +31514 fous +31515 foutez +31516 foutu +31521 foutus +31522 fox +31523 foyer +31524 foyers +31525 fp +31526 fq +31531 fr +31532 frac +31533 fracas +31534 fracs +31535 frai +31536 fraie +31541 frais +31542 fraise +31543 franc +31544 fraser +31545 fraya +31546 fraye +31551 frayer +31552 frayes +31553 frayez +31554 frein +31555 freina +31556 freins +31561 fret +31562 frets +31563 friche +31564 frigo +31565 frigos +31566 frime +31611 frimer +31612 fripa +31613 fripe +31614 friper +31615 frira +31616 frirai +31621 friras +31622 frire +31623 frirez +31624 fris +31625 frisa +31626 frise +31631 frisez +31632 frit +31633 frite +31634 frits +31635 froc +31636 froid +31641 front +31642 fronts +31643 frugal +31644 fruit +31645 fs +31646 ft +31651 fu +31652 fucus +31653 fuel +31654 fuels +31655 fugace +31656 fugue +31661 fuguer +31662 fui +31663 fuie +31664 fuies +31665 fuir +31666 fuira +32111 fuirai +32112 fuirez +32113 fuis +32114 fuit +32115 fuite +32116 fuites +32121 full +32122 fulls +32123 fuma +32124 fumage +32125 fumait +32126 fume +32131 fumer +32132 fumes +32133 fumet +32134 fumets +32135 fumeur +32136 fumez +32141 fumier +32142 fumoir +32143 fur +32144 furet +32145 furets +32146 fureur +32151 furia +32152 furie +32153 furies +32154 fusa +32155 fusain +32156 fusait +32161 fuse +32162 fuseau +32163 fuser +32164 fusera +32165 fusil +32166 fut +32211 futur +32212 fuyais +32213 fuyant +32214 fuyez +32215 fuyiez +32216 fuyons +32221 fv +32222 fw +32223 fx +32224 fy +32225 fz +32226 g +32231 ga +32232 gaffe +32233 gaffer +32234 gaffes +32235 gag +32236 gage +32241 gagea +32242 gager +32243 gages +32244 gagez +32245 gagna +32246 gagne +32251 gagner +32252 gagnes +32253 gagnez +32254 gags +32255 gai +32256 gaie +32261 gaies +32262 gain +32263 gaine +32264 gainer +32265 gaines +32266 gains +32311 gais +32312 gala +32313 galant +32314 galas +32315 galbe +32316 galber +32321 galbes +32322 gale +32323 gales +32324 galet +32325 galeux +32326 galon +32331 galop +32332 gamba +32333 gambas +32334 gamet +32335 gamets +32336 gamin +32341 gamine +32342 gamins +32343 gamme +32344 gang +32345 gangs +32346 gangue +32351 ganse +32352 ganses +32353 gant +32354 gants +32355 gara +32356 garant +32361 garce +32362 garda +32363 garde +32364 gardes +32365 gardez +32366 gare +32411 garer +32412 garera +32413 gares +32414 garez +32415 gariez +32416 garni +32421 garnie +32422 garnit +32423 gars +32424 gaule +32425 gava +32426 gavais +32431 gavant +32432 gave +32433 gaver +32434 gaves +32435 gavez +32436 gavial +32441 gaviez +32442 gaz +32443 gazage +32444 gaze +32445 gazer +32446 gazes +32451 gazeux +32452 gazon +32453 gazons +32454 gb +32455 gc +32456 gd +32461 ge +32462 geai +32463 geais +32464 geigne +32465 geins +32466 geint +32511 gel +32512 gela +32513 gelais +32514 gelait +32515 gelant +32516 geler +32521 gelez +32522 geliez +32523 gelons +32524 gels +32525 gemme +32526 gemmer +32531 gendre +32532 genet +32533 genets +32534 genou +32535 genre +32536 genres +32541 gens +32542 gentes +32543 gentil +32544 gerbe +32545 gerber +32546 gerbes +32551 gerce +32552 germa +32553 germe +32554 geste +32555 gf +32556 gg +32561 ggg +32562 gggg +32563 gh +32564 ghi +32565 gi +32566 gibbon +32611 gibet +32612 gibets +32613 gicla +32614 gicle +32615 gifla +32616 gifle +32621 gifler +32622 giflez +32623 gigot +32624 gigue +32625 gilet +32626 gilets +32631 gin +32632 gins +32633 girl +32634 girls +32635 giron +32636 gisait +32641 gisant +32642 gitan +32643 gitane +32644 gitans +32645 givra +32646 givre +32651 givrer +32652 gj +32653 gk +32654 gl +32655 glabre +32656 glace +32661 glacer +32662 glaces +32663 glana +32664 gland +32665 glane +32666 glaner +33111 glanes +33112 glas +33113 glatir +33114 globe +33115 globes +33116 gloire +33121 glose +33122 gloser +33123 gloses +33124 glu +33125 gluant +33126 glus +33131 gm +33132 gn +33133 gnome +33134 gnon +33135 gnons +33136 gnou +33141 gnous +33142 go +33143 goal +33144 goals +33145 goba +33146 gobait +33151 gobe +33152 gober +33153 gobes +33154 gobez +33155 gobons +33156 goder +33161 godet +33162 goitre +33163 golden +33164 golf +33165 golfe +33166 golfs +33211 gomma +33212 gomme +33213 gommez +33214 gond +33215 gonds +33216 gonfle +33221 gong +33222 gongs +33223 goret +33224 gorge +33225 gorger +33226 gorges +33231 gosier +33232 gosse +33233 gouge +33234 gouges +33235 goulot +33236 goulu +33241 goulue +33242 gourd +33243 gourde +33244 gourds +33245 gourou +33246 gousse +33251 gp +33252 gq +33253 gr +33254 gracia +33255 grade +33256 gradin +33261 grain +33262 grains +33263 grand +33264 grandi +33265 grappe +33266 gras +33311 grasse +33312 gratin +33313 gratis +33314 gratta +33315 grava +33316 grave +33321 graver +33322 graves +33323 gravi +33324 gravie +33325 gravis +33326 gravit +33331 grec +33332 grecs +33333 greffe +33334 grenat +33335 grener +33336 grenu +33341 greva +33342 grevez +33343 grief +33344 griefs +33345 griffe +33346 griffu +33351 gril +33352 grill +33353 grils +33354 grimer +33355 grimpa +33356 grimpe +33361 griot +33362 grippe +33363 gris +33364 grisa +33365 grisbi +33366 grise +33411 grises +33412 grive +33413 grives +33414 grog +33415 grogna +33416 grogs +33421 groin +33422 gronda +33423 groom +33424 gros +33425 grosse +33426 grotte +33431 groupa +33432 grouse +33433 gruau +33434 gruaux +33435 grue +33436 grues +33441 gruger +33442 grume +33443 gs +33444 gt +33445 gu +33446 guano +33451 guerre +33452 guet +33453 guette +33454 gueuse +33455 gui +33456 guida +33461 guide +33462 guider +33463 guidez +33464 guigne +33465 guimpe +33466 guis +33511 guise +33512 gv +33513 gw +33514 gx +33515 gy +33516 gypse +33521 gz +33522 h +33523 ha +33524 habile +33525 habit +33526 habita +33531 habits +33532 hacha +33533 hache +33534 hachez +33535 hachis +33536 hacker +33541 haie +33542 haies +33543 haine +33544 hais +33545 hait +33546 haler +33551 haleta +33552 hall +33553 halle +33554 halls +33555 halo +33556 halos +33561 halte +33562 haltes +33563 hamac +33564 hampe +33565 hampes +33566 hanta +33611 hante +33612 hantez +33613 happa +33614 happe +33615 haras +33616 harder +33621 hardi +33622 harem +33623 harems +33624 hargne +33625 haro +33626 haros +33631 harpe +33632 harpes +33633 harpie +33634 hase +33635 hases +33636 haussa +33641 hausse +33642 haut +33643 haute +33644 hautes +33645 hauts +33646 havane +33651 haver +33652 havre +33653 havres +33654 hayon +33655 hayons +33656 hb +33661 hc +33662 hd +33663 he +33664 heaume +33665 hein +33666 hennir +34111 herbe +34112 herber +34113 herbes +34114 hernie +34115 herse +34116 hertz +34121 heur +34122 heure +34123 heures +34124 heurt +34125 heurta +34126 heurts +34131 hf +34132 hg +34133 hh +34134 hhh +34135 hhhh +34136 hi +34141 hiatus +34142 hibou +34143 hic +34144 hideux +34145 hie +34146 hier +34151 hies +34152 hij +34153 hilare +34154 hindi +34155 hippie +34156 hippy +34161 hissa +34162 hisse +34163 hisser +34164 hiver +34165 hj +34166 hk +34211 hl +34212 hm +34213 hn +34214 ho +34215 hobby +34216 hocha +34221 hoche +34222 hocher +34223 hochet +34224 hockey +34225 homard +34226 home +34231 homme +34232 honni +34233 honnie +34234 honnis +34235 honora +34236 honte +34241 horde +34242 horion +34243 hors +34244 hostie +34245 hotte +34246 hotter +34251 houe +34252 houer +34253 houes +34254 houle +34255 hourra +34256 housse +34261 houx +34262 hp +34263 hq +34264 hr +34265 hs +34266 ht +34311 hu +34312 hua +34313 huais +34314 huait +34315 huant +34316 huche +34321 hucher +34322 hue +34323 huent +34324 huer +34325 huera +34326 huerez +34331 hues +34332 huez +34333 hui +34334 huiez +34335 huila +34336 huile +34341 huilez +34342 huis +34343 huit +34344 huma +34345 humait +34346 humant +34351 hume +34352 hument +34353 humer +34354 humes +34355 humeur +34356 humez +34361 humons +34362 humour +34363 humus +34364 hune +34365 hunes +34366 huons +34411 huotte +34412 huppe +34413 hurla +34414 hurle +34415 hurrah +34416 hutte +34421 huttes +34422 hv +34423 hw +34424 hx +34425 hy +34426 hydre +34431 hydres +34432 hymen +34433 hymens +34434 hymne +34435 hz +34436 i +34441 ia +34442 iambe +34443 ib +34444 ibis +34445 ic +34446 ici +34451 ictus +34452 id +34453 idem +34454 ides +34455 idiome +34456 idiot +34461 idoine +34462 idole +34463 idoles +34464 idylle +34465 ie +34466 if +34511 ifs +34512 ig +34513 igloo +34514 igloos +34515 igname +34516 ignare +34521 ignore +34522 iguane +34523 ih +34524 ii +34525 iii +34526 iiii +34531 ij +34532 ijk +34533 ik +34534 il +34535 ilote +34536 ils +34541 im +34542 image +34543 images +34544 imbibe +34545 imbu +34546 imbue +34551 imbus +34552 imita +34553 imite +34554 imitez +34555 imper +34556 impie +34561 impies +34562 impoli +34563 impur +34564 impure +34565 impurs +34566 imputa +34611 impute +34612 in +34613 inapte +34614 inca +34615 incas +34616 incite +34621 index +34622 indexa +34623 indexe +34624 indu +34625 indue +34626 induis +34631 indus +34632 inepte +34633 inerte +34634 infime +34635 infini +34636 influa +34641 influx +34642 infusa +34643 infuse +34644 ingrat +34645 inhuma +34646 inique +34651 initie +34652 injure +34653 innove +34654 inox +34655 input +34656 instar +34661 insu +34662 inter +34663 intima +34664 invita +34665 invite +34666 io +35111 iode +35112 ioder +35113 iodes +35114 ion +35115 ions +35116 iota +35121 iouler +35122 ip +35123 iq +35124 ir +35125 ira +35126 irai +35131 irais +35132 irait +35133 iras +35134 irez +35135 iriez +35136 irions +35141 iris +35142 ironie +35143 irons +35144 iront +35145 irrita +35146 irrite +35151 is +35152 isard +35153 isba +35154 isbas +35155 islam +35156 isola +35161 isole +35162 isoler +35163 issu +35164 issue +35165 issus +35166 it +35211 item +35212 iu +35213 iv +35214 ivraie +35215 ivre +35216 ivres +35221 iw +35222 ix +35223 iy +35224 iz +35225 j +35226 ja +35231 jabler +35232 jabot +35233 jabots +35234 jade +35235 jades +35236 jadis +35241 jaguar +35242 jais +35243 jalon +35244 jalons +35245 jambe +35246 jambes +35251 jambon +35252 jante +35253 jantes +35254 jardin +35255 jarre +35256 jarret +35261 jars +35262 jas +35263 jasa +35264 jasait +35265 jasant +35266 jase +35311 jasent +35312 jaser +35313 jasera +35314 jasper +35315 jatte +35316 jattes +35321 jauge +35322 jauger +35323 jauges +35324 jaugez +35325 jaune +35326 jaunes +35331 jauni +35332 jaunis +35333 java +35334 javas +35335 jazz +35336 jb +35341 jc +35342 jd +35343 je +35344 jean +35345 jeans +35346 jerez +35351 jerk +35352 jerker +35353 jet +35354 jeta +35355 jeter +35356 jetez +35361 jetiez +35362 jeton +35363 jetons +35364 jets +35365 jette +35366 jeu +35411 jeudi +35412 jeun +35413 jeune +35414 jeunes +35415 jeux +35416 jf +35421 jg +35422 jh +35423 ji +35424 jj +35425 jjj +35426 jjjj +35431 jk +35432 jkl +35433 jl +35434 jm +35435 jn +35436 jo +35441 job +35442 jobard +35443 jobs +35444 jockey +35445 joie +35446 joies +35451 joins +35452 joint +35453 jointe +35454 joker +35455 jokers +35456 joli +35461 jolie +35462 jolis +35463 jonc +35464 joncs +35465 jongla +35466 jongle +35511 jonque +35512 joua +35513 jouait +35514 joual +35515 jouant +35516 joue +35521 jouer +35522 jouera +35523 joues +35524 jouet +35525 joueur +35526 jouez +35531 joug +35532 jougs +35533 joui +35534 jouiez +35535 jouir +35536 jouira +35541 jouis +35542 jouit +35543 joule +35544 joules +35545 jour +35546 jours +35551 joute +35552 joutes +35553 jouxte +35554 joyau +35555 joyaux +35556 joyeux +35561 jp +35562 jq +35563 jr +35564 js +35565 jt +35566 ju +35611 jubila +35612 jubile +35613 judas +35614 judo +35615 judoka +35616 juge +35621 jugea +35622 juger +35623 juges +35624 jugez +35625 jugiez +35626 juif +35631 juifs +35632 juin +35633 juive +35634 juives +35635 jumeau +35636 jumela +35641 junior +35642 junte +35643 juntes +35644 jupe +35645 jupes +35646 jupon +35651 jura +35652 jurais +35653 jurait +35654 jurant +35655 jure +35656 jurer +35661 jures +35662 jurez +35663 juron +35664 jurons +35665 jury +35666 jurys +36111 jus +36112 jusque +36113 juste +36114 jute +36115 juter +36116 juteux +36121 jv +36122 jw +36123 jx +36124 jy +36125 jz +36126 k +36131 ka +36132 kaki +36133 kakis +36134 kaolin +36135 kapok +36136 karma +36141 kart +36142 karts +36143 kasher +36144 kayak +36145 kb +36146 kc +36151 kd +36152 ke +36153 kf +36154 kg +36155 kh +36156 ki +36161 kif +36162 kilo +36163 kilos +36164 kilt +36165 kilts +36166 kimono +36211 kir +36212 kit +36213 kits +36214 kiwi +36215 kiwis +36216 kj +36221 kk +36222 kkk +36223 kkkk +36224 kl +36225 klm +36226 km +36231 kn +36232 ko +36233 kola +36234 kolas +36235 kp +36236 kq +36241 kr +36242 krach +36243 kraft +36244 krak +36245 kraks +36246 ks +36251 kt +36252 ku +36253 kv +36254 kvas +36255 kw +36256 kwas +36261 kx +36262 ky +36263 kyste +36264 kystes +36265 kz +36266 l +36311 la +36312 label +36313 labial +36314 lac +36315 lace +36316 lacer +36321 laces +36322 lacet +36323 lacets +36324 lacez +36325 laciez +36326 lacis +36331 lacs +36332 lad +36333 ladre +36334 ladres +36335 lads +36336 lagon +36341 lagons +36342 lagune +36343 laid +36344 laide +36345 laids +36346 laie +36351 laies +36352 laine +36353 lainer +36354 laisse +36355 lait +36356 laits +36361 lama +36362 lamas +36363 lame +36364 lamer +36365 lames +36366 lamine +36411 lampe +36412 lance +36413 lances +36414 land +36415 landau +36416 lande +36421 landes +36422 lange +36423 langue +36424 laper +36425 lapin +36426 lapine +36431 lapis +36432 laps +36433 lapsus +36434 laque +36435 lard +36436 larder +36441 lardon +36442 large +36443 largua +36444 largue +36445 larme +36446 larmes +36451 larron +36452 larve +36453 las +36454 lascar +36455 lascif +36456 laser +36461 lassa +36462 lasse +36463 lasses +36464 lassez +36465 lasso +36466 lassos +36511 latent +36512 latex +36513 latin +36514 latine +36515 latins +36516 latte +36521 lattes +36522 lava +36523 lavage +36524 lavait +36525 lave +36526 laver +36531 laves +36532 lavez +36533 lavis +36534 lavoir +36535 layer +36536 lazzi +36541 lb +36542 lc +36543 ld +36544 le +36545 leader +36546 ledit +36551 legs +36552 lent +36553 lente +36554 lents +36555 les +36556 lest +36561 lesta +36562 leste +36563 lestes +36564 lests +36565 let +36566 lettre +36611 leur +36612 leurs +36613 leva +36614 levage +36615 levain +36616 levais +36621 levait +36622 levant +36623 lever +36624 levers +36625 levez +36626 levons +36631 levure +36632 lf +36633 lg +36634 lh +36635 li +36636 lia +36641 liais +36642 liait +36643 liane +36644 lianes +36645 liant +36646 liants +36651 libre +36652 libres +36653 lice +36654 licher +36655 licite +36656 licol +36661 licols +36662 licou +36663 licous +36664 lie +36665 lied +36666 lieds +41111 lien +41112 liens +41113 lient +41114 lier +41115 liera +41116 lierai +41121 lies +41122 lieu +41123 lieue +41124 lieuse +41125 lieux +41126 liez +41131 lift +41132 lifter +41133 lifts +41134 ligne +41135 ligner +41136 ligote +41141 ligua +41142 ligue +41143 liguer +41144 liguez +41145 liiez +41146 liions +41151 lilas +41152 lima +41153 limais +41154 limait +41155 lime +41156 limer +41161 limera +41162 limes +41163 limez +41164 limier +41165 limite +41166 limoge +41211 limon +41212 limons +41213 lin +41214 linge +41215 lino +41216 linos +41221 lion +41222 lionne +41223 lions +41224 lipide +41225 lippe +41226 lippu +41231 lippue +41232 lira +41233 lirai +41234 liras +41235 lire +41236 lires +41241 lirez +41242 liriez +41243 lis +41244 lisait +41245 lisant +41246 lise +41251 lisent +41252 lises +41253 lisez +41254 lissa +41255 lisse +41256 lisses +41261 lista +41262 liste +41263 listez +41264 lit +41265 liter +41266 litre +41311 litres +41312 lits +41313 livra +41314 livre +41315 livrer +41316 lj +41321 lk +41322 ll +41323 lll +41324 llll +41325 lm +41326 lmn +41331 ln +41332 lo +41333 lob +41334 lobby +41335 lobe +41336 lober +41341 lobes +41342 lobs +41343 local +41344 locale +41345 locher +41346 loden +41351 loess +41352 lofer +41353 loge +41354 logea +41355 logent +41356 loger +41361 loges +41362 logeur +41363 logez +41364 logiez +41365 logis +41366 logo +41411 logos +41412 loi +41413 loin +41414 loir +41415 loirs +41416 lois +41421 long +41422 longe +41423 longer +41424 longes +41425 longs +41426 lopin +41431 lopins +41432 loque +41433 lors +41434 lot +41435 lote +41436 lotes +41441 loti +41442 lotie +41443 loties +41444 lotir +41445 lotira +41446 lotis +41451 lotit +41452 loto +41453 lots +41454 lotte +41455 lottes +41456 lotus +41461 loua +41462 louage +41463 loue +41464 louer +41465 loues +41466 loueur +41511 louez +41512 louiez +41513 loup +41514 loupe +41515 louper +41516 loups +41521 lourd +41522 lourds +41523 loutre +41524 louve +41525 louves +41526 lover +41531 loyal +41532 loyale +41533 loyaux +41534 loyer +41535 loyers +41536 lp +41541 lq +41542 lr +41543 ls +41544 lt +41545 lu +41546 lubie +41551 lucide +41552 lucre +41553 lue +41554 lues +41555 lueur +41556 luge +41561 luger +41562 luges +41563 lui +41564 luira +41565 luire +41566 luise +41611 luit +41612 lump +41613 lumps +41614 lunch +41615 lundi +41616 lune +41621 lunes +41622 lupin +41623 lupins +41624 lurent +41625 luron +41626 lurons +41631 lus +41632 lustra +41633 lustre +41634 lut +41635 luter +41636 luth +41641 luths +41642 lutin +41643 lutta +41644 lutte +41645 lutter +41646 lux +41651 luxa +41652 luxant +41653 luxe +41654 luxent +41655 luxer +41656 luxes +41661 luxez +41662 luxiez +41663 luxure +41664 lv +41665 lw +41666 lx +42111 ly +42112 lynx +42113 lyre +42114 lyres +42115 lys +42116 lyse +42121 lyses +42122 lz +42123 m +42124 ma +42125 mach +42126 madone +42131 mafia +42132 mage +42133 mages +42134 magie +42135 magma +42136 magner +42141 magot +42142 mai +42143 maille +42144 main +42145 mains +42146 maint +42151 mainte +42152 maints +42153 maire +42154 maires +42155 mairie +42156 mais +42161 major +42162 mal +42163 malaxa +42164 malice +42165 malin +42166 malins +42211 malle +42212 malles +42213 malt +42214 malter +42215 malus +42216 maman +42221 mamans +42222 man +42223 manche +42224 manda +42225 mandat +42226 mande +42231 mander +42232 mandez +42233 manga +42234 mange +42235 manger +42236 mania +42241 manie +42242 maniez +42243 manioc +42244 manne +42245 manqua +42246 mante +42251 mantes +42252 manuel +42253 maquer +42254 maquis +42255 marbre +42256 marc +42261 marche +42262 mardi +42263 mare +42264 mares +42265 marge +42266 marges +42311 mari +42312 maria +42313 marie +42314 maries +42315 mariez +42316 marin +42321 marina +42322 marine +42323 maris +42324 mark +42325 marks +42326 marne +42331 marner +42332 marque +42333 marra +42334 marre +42335 marres +42336 marri +42341 marrie +42342 marris +42343 mars +42344 marte +42345 martel +42346 martes +42351 martre +42352 martyr +42353 mas +42354 masqua +42355 massa +42356 masse +42361 masser +42362 massue +42363 mastic +42364 mastoc +42365 masure +42366 mat +42411 match +42412 matchs +42413 mate +42414 mater +42415 mates +42416 math +42421 maths +42422 matin +42423 matir +42424 mats +42425 mature +42426 mauve +42431 mauves +42432 mauvis +42433 maux +42434 maxima +42435 maya +42436 mayas +42441 mazer +42442 mazout +42443 mb +42444 mc +42445 md +42446 me +42451 melon +42452 men +42453 mena +42454 menace +42455 menait +42456 menant +42461 mendia +42462 mendie +42463 mener +42464 meneur +42465 menez +42466 menons +42511 mens +42512 ment +42513 mente +42514 mentez +42515 menti +42516 menton +42521 mentor +42522 menu +42523 menue +42524 menues +42525 menuet +42526 menus +42531 mer +42532 merci +42533 mercis +42534 merder +42535 merise +42536 merlan +42541 merle +42542 merlu +42543 mers +42544 mes +42545 mess +42546 messe +42551 messes +42552 messie +42553 met +42554 mets +42555 mette +42556 mettes +42561 meubla +42562 meugle +42563 meule +42564 meure +42565 meurs +42566 meurt +42611 meus +42612 meut +42613 meute +42614 meutes +42615 meuve +42616 mf +42621 mg +42622 mh +42623 mi +42624 miasme +42625 mica +42626 micas +42631 miche +42632 miches +42633 micmac +42634 micro +42635 micron +42636 micros +42641 midi +42642 midis +42643 mie +42644 miel +42645 miels +42646 mien +42651 mienne +42652 miens +42653 mieux +42654 mignon +42655 migre +42656 migrer +42661 migrez +42662 mijota +42663 mil +42664 milan +42665 mile +42666 miles +43111 milite +43112 mille +43113 milles +43114 millet +43115 mima +43116 mimait +43121 mimant +43122 mime +43123 mimer +43124 mimes +43125 mimez +43126 min +43131 mina +43132 minais +43133 mince +43134 minci +43135 mincie +43136 mincis +43141 mine +43142 minent +43143 miner +43144 mines +43145 minet +43146 mineur +43151 minez +43152 minime +43153 minois +43154 minons +43155 minora +43156 minore +43161 minuit +43162 minus +43163 mioche +43164 mira +43165 mirage +43166 mirait +43211 mirant +43212 mire +43213 mirent +43214 mirer +43215 mirera +43216 mires +43221 mirez +43222 miroir +43223 mis +43224 misa +43225 misait +43226 misant +43231 mise +43232 miser +43233 misera +43234 mises +43235 misez +43236 misiez +43241 misons +43242 miss +43243 missel +43244 misses +43245 mit +43246 mita +43251 mite +43252 mitent +43253 miter +43254 mitera +43255 mites +43256 mitre +43261 mitron +43262 mixa +43263 mixage +43264 mixais +43265 mixait +43266 mixant +43311 mixe +43312 mixent +43313 mixer +43314 mixes +43315 mixez +43316 mixte +43321 mixtes +43322 mj +43323 mk +43324 ml +43325 mm +43326 mmm +43331 mmmm +43332 mn +43333 mno +43334 mo +43335 moche +43336 modal +43341 modale +43342 mode +43343 modela +43344 modem +43345 modes +43346 modula +43351 modus +43352 moelle +43353 moi +43354 moine +43355 moines +43356 moins +43361 moire +43362 mois +43363 moisi +43364 moisie +43365 moisir +43366 moite +43411 moites +43412 moitir +43413 moka +43414 mokas +43415 mol +43416 molle +43421 mollet +43422 molli +43423 moment +43424 momie +43425 momies +43426 mon +43431 monde +43432 monder +43433 mondes +43434 mont +43435 monta +43436 monte +43441 montra +43442 monts +43443 moqua +43444 moque +43445 moquez +43446 moral +43451 mord +43452 morde +43453 mordre +43454 mords +43455 mordu +43456 mordue +43461 morgue +43462 morne +43463 mornes +43464 mors +43465 morse +43466 morses +43511 mort +43512 morte +43513 mortel +43514 morts +43515 morue +43516 morve +43521 mot +43522 motel +43523 motif +43524 motiva +43525 moto +43526 motos +43531 mots +43532 motte +43533 mou +43534 moucha +43535 mouche +43536 moud +43541 moudra +43542 moudre +43543 mouds +43544 moue +43545 moues +43546 moula +43551 moule +43552 moulin +43553 moulu +43554 mourez +43555 mouron +43556 mourut +43561 mous +43562 moussa +43563 mousse +43564 mouvez +43565 moyen +43566 moyeu +43611 moyeux +43612 mp +43613 mq +43614 mr +43615 ms +43616 mt +43621 mu +43622 mua +43623 muais +43624 muait +43625 muant +43626 mue +43631 muent +43632 muer +43633 muera +43634 muerez +43635 mues +43636 muet +43641 muets +43642 muez +43643 mufle +43644 mugi +43645 mugir +43646 mugis +43651 mugit +43652 muiez +43653 mule +43654 mules +43655 mulet +43656 mulot +43661 muni +43662 munie +43663 munir +43664 munira +43665 munis +43666 munit +44111 muons +44112 mur +44113 mura +44114 murais +44115 mural +44116 murale +44121 murals +44122 mure +44123 murent +44124 murer +44125 mures +44126 muret +44131 murez +44132 muriez +44133 murons +44134 murs +44135 mus +44136 musc +44141 muscat +44142 muscle +44143 muscs +44144 muse +44145 museau +44146 muser +44151 muses +44152 musser +44153 muta +44154 mutant +44155 mute +44156 muter +44161 mutera +44162 mutes +44163 mutez +44164 mutiez +44165 mutile +44166 mutin +44211 mutina +44212 mv +44213 mw +44214 mx +44215 my +44216 myope +44221 myrrhe +44222 mythe +44223 mz +44224 n +44225 na +44226 nabab +44231 nababs +44232 nabot +44233 nabote +44234 nacre +44235 nacres +44236 nage +44241 nagea +44242 nager +44243 nages +44244 nagez +44245 nain +44246 naine +44251 nains +44252 nais +44253 naja +44254 najas +44255 nanan +44256 nanti +44261 nantir +44262 nantis +44263 nantit +44264 napalm +44265 nappe +44266 naquit +44311 narra +44312 narre +44313 narres +44314 narrez +44315 naseau +44316 nasse +44321 natal +44322 natale +44323 natif +44324 nation +44325 natte +44326 natter +44331 nattes +44332 naval +44333 navals +44334 navet +44335 navets +44336 navire +44341 navra +44342 navre +44343 navrer +44344 navrez +44345 nazi +44346 nazie +44351 nazies +44352 nazis +44353 nb +44354 nc +44355 nd +44356 ne +44361 nectar +44362 nef +44363 nefs +44364 negro +44365 neige +44366 neiger +44411 nenni +44412 nerf +44413 nerfs +44414 nervi +44415 nervis +44416 net +44421 nets +44422 nette +44423 nettes +44424 neuf +44425 neufs +44426 neutre +44431 neuve +44432 neveu +44433 nez +44434 nf +44435 ng +44436 nh +44441 ni +44442 nia +44443 niable +44444 niais +44445 niait +44446 niant +44451 nicha +44452 niche +44453 nicher +44454 niches +44455 nid +44456 nids +44461 nie +44462 nient +44463 nier +44464 niera +44465 nierai +44466 nies +44511 niez +44512 nigaud +44513 niiez +44514 nimber +44515 ninja +44516 ninjas +44521 nions +44522 nippe +44523 nipper +44524 nique +44525 niveau +44526 nivela +44531 nj +44532 nk +44533 nl +44534 nm +44535 nn +44536 nnn +44541 nnnn +44542 no +44543 noble +44544 nobles +44545 noce +44546 noces +44551 nocif +44552 nocifs +44553 nodule +44554 noeud +44555 noie +44556 noiera +44561 noies +44562 noir +44563 noire +44564 noires +44565 noirs +44566 noise +44611 noises +44612 noix +44613 nom +44614 nomade +44615 nomma +44616 nomme +44621 nommer +44622 noms +44623 non +44624 nonce +44625 nonne +44626 nop +44631 nord +44632 normal +44633 norme +44634 normes +44635 nos +44636 nota +44641 notais +44642 notait +44643 note +44644 notent +44645 noter +44646 notera +44651 notes +44652 notez +44653 notice +44654 notons +44655 notre +44656 notule +44661 noua +44662 noue +44663 nouer +44664 nouera +44665 noues +44666 noueux +45111 nouez +45112 nouiez +45113 nouons +45114 nourri +45115 nous +45116 nover +45121 noya +45122 noyau +45123 noyaux +45124 noyer +45125 noyez +45126 noyiez +45131 noyons +45132 np +45133 nq +45134 nr +45135 ns +45136 nt +45141 nu +45142 nuage +45143 nubile +45144 nue +45145 nues +45146 nui +45151 nuira +45152 nuiras +45153 nuire +45154 nuirez +45155 nuis +45156 nuise +45161 nuit +45162 nuits +45163 nul +45164 nulle +45165 nulles +45166 nuls +45211 nuque +45212 nurse +45213 nus +45214 nv +45215 nw +45216 nx +45221 ny +45222 nylon +45223 nz +45224 o +45225 oa +45226 oasis +45231 ob +45232 obier +45233 objet +45234 objets +45235 oblige +45236 obole +45241 obture +45242 obtus +45243 obus +45244 obvier +45245 oc +45246 occupe +45251 ocre +45252 ocrer +45253 ocres +45254 octal +45255 octane +45256 octave +45261 octet +45262 octroi +45263 od +45264 ode +45265 odes +45266 odeur +45311 odieux +45312 oe +45313 oeil +45314 oeuf +45315 oeufs +45316 oeuvre +45321 of +45322 offert +45323 office +45324 offre +45325 offres +45326 offrez +45331 og +45332 ogive +45333 ogives +45334 ogre +45335 ogres +45336 oh +45341 ohm +45342 ohms +45343 oi +45344 oie +45345 oies +45346 oignon +45351 oisif +45352 oisifs +45353 oison +45354 oisons +45355 oj +45356 ok +45361 okapi +45362 ol +45363 olive +45364 olives +45365 om +45366 ombre +45411 omet +45412 omets +45413 omette +45414 omis +45415 omise +45416 omit +45421 on +45422 onagre +45423 once +45424 onces +45425 oncle +45426 oncles +45431 onde +45432 ondes +45433 ondula +45434 ongle +45435 ont +45436 onyx +45441 onze +45442 oo +45443 ooo +45444 oooo +45445 op +45446 opale +45451 opales +45452 opaque +45453 opina +45454 opine +45455 opiner +45456 opines +45461 opium +45462 opq +45463 opta +45464 opte +45465 optent +45466 opter +45511 optera +45512 optes +45513 optez +45514 optima +45515 option +45516 opus +45521 oq +45522 or +45523 orage +45524 oral +45525 orale +45526 oraux +45531 orc +45532 orcs +45533 ordre +45534 ordure +45535 ores +45536 orge +45541 orges +45542 orgie +45543 orgies +45544 orgue +45545 orme +45546 ormeau +45551 ormes +45552 orna +45553 ornais +45554 ornant +45555 orne +45556 ornent +45561 orner +45562 ornes +45563 ornez +45564 orniez +45565 ornons +45566 oronge +45611 orque +45612 ors +45613 orteil +45614 ortie +45615 orvet +45616 os +45621 osa +45622 osais +45623 osait +45624 osant +45625 oscar +45626 oscars +45631 ose +45632 osent +45633 oser +45634 osera +45635 oses +45636 osez +45641 osier +45642 osiers +45643 osiez +45644 osions +45645 osmose +45646 osons +45651 ot +45652 otage +45653 otages +45654 otarie +45655 otite +45656 otites +45661 ou +45662 ouate +45663 ouater +45664 oubli +45665 oublis +45666 oued +46111 oueds +46112 ouest +46113 oui +46114 ourdi +46115 ourdie +46116 ourdis +46121 ourler +46122 ours +46123 ourse +46124 outil +46125 outils +46126 outra +46131 outre +46132 outrez +46133 ouvre +46134 ouvrer +46135 ouvrez +46136 ov +46141 ovaire +46142 ovale +46143 ovales +46144 ovine +46145 ovni +46146 ovnis +46151 ovule +46152 ovuler +46153 ow +46154 ox +46155 oxyda +46156 oxyde +46161 oxyder +46162 oy +46163 oz +46164 ozone +46165 ozones +46166 p +46211 pa +46212 pacha +46213 pack +46214 packs +46215 pacte +46216 page +46221 pages +46222 pagina +46223 pagine +46224 pagne +46225 pagode +46226 paie +46231 paient +46232 paies +46233 paille +46234 pain +46235 pains +46236 pair +46241 paire +46242 paires +46243 pairs +46244 paix +46245 pal +46246 palace +46251 palan +46252 pale +46253 pales +46254 palet +46255 palier +46256 palme +46261 palmes +46262 palot +46263 palpa +46264 palpe +46265 palpes +46266 palpez +46311 pals +46312 pampa +46313 pampas +46314 pan +46315 panda +46316 panel +46321 panels +46322 paner +46323 panne +46324 pannes +46325 pans +46326 pansa +46331 panse +46332 panser +46333 pansez +46334 pantin +46335 paon +46336 paonne +46341 paons +46342 papa +46343 papal +46344 papale +46345 papas +46346 papaux +46351 pape +46352 papes +46353 papier +46354 paquet +46355 par +46356 para +46361 parada +46362 parade +46363 parafe +46364 paras +46365 parc +46366 parce +46411 parcs +46412 pare +46413 pareil +46414 parer +46415 pares +46416 parez +46421 pari +46422 paria +46423 parias +46424 parie +46425 paris +46426 parla +46431 parle +46432 parles +46433 parmi +46434 paroi +46435 pars +46436 part +46441 parte +46442 parti +46443 partie +46444 partit +46445 parts +46446 paru +46451 parue +46452 parure +46453 parus +46454 parut +46455 pas +46456 pascal +46461 passa +46462 passe +46463 passer +46464 passes +46465 passez +46466 passif +46511 patent +46512 patin +46513 patine +46514 patio +46515 patte +46516 paume +46521 paumes +46522 pause +46523 pauses +46524 pava +46525 pavait +46526 pavant +46531 pave +46532 pavent +46533 paver +46534 paves +46535 paveur +46536 pavez +46541 pavois +46542 pavons +46543 pavot +46544 paya +46545 paye +46546 payent +46551 payer +46552 payera +46553 payes +46554 payeur +46555 payez +46556 payiez +46561 pays +46562 payse +46563 pb +46564 pc +46565 pd +46566 pe +46611 peau +46612 peaux +46613 peina +46614 peine +46615 peiner +46616 peines +46621 peinez +46622 peins +46623 peint +46624 peints +46625 pela +46626 pelage +46631 peler +46632 pelez +46633 peliez +46634 pelle +46635 pelles +46636 pelons +46641 pelote +46642 pencha +46643 penche +46644 pend +46645 pende +46646 pendes +46651 pendra +46652 pends +46653 pendu +46654 pendue +46655 pensa +46656 pense +46661 penses +46662 pensez +46663 pensum +46664 pente +46665 perce +46666 perd +51111 perde +51112 perdre +51113 perds +51114 perdu +51115 perdue +51116 perla +51121 perle +51122 permet +51123 permis +51124 permit +51125 perron +51126 pers +51131 perse +51132 perses +51133 perte +51134 pesa +51135 pesage +51136 pesais +51141 peser +51142 peseta +51143 pesez +51144 pesons +51145 pesta +51146 peste +51151 pestes +51152 pet +51153 petit +51154 peton +51155 petons +51156 pets +51161 peu +51162 peupla +51163 peuple +51164 peur +51165 peurs +51166 peut +51211 peux +51212 pf +51213 pg +51214 ph +51215 phare +51216 phares +51221 phase +51222 phases +51223 phobie +51224 phone +51225 phono +51226 phoque +51231 photo +51232 pi +51233 piaffe +51234 piano +51235 pic +51236 pics +51241 pie +51242 pied +51243 pieds +51244 pies +51245 pieu +51246 pieuse +51251 pieux +51252 pige +51253 piger +51254 piges +51255 pigne +51256 pignon +51261 pila +51262 pilait +51263 pile +51264 piler +51265 piles +51266 pileux +51311 pilez +51312 piliez +51313 pilla +51314 pille +51315 pillez +51316 pilon +51321 pilou +51322 pilous +51323 pilule +51324 pin +51325 pince +51326 pins +51331 pinte +51332 piolet +51333 pion +51334 pions +51335 pipe +51336 pipeau +51341 piper +51342 pipes +51343 piqua +51344 pique +51345 piquer +51346 piques +51351 pire +51352 pires +51353 pis +51354 pisser +51355 pista +51356 piste +51361 pister +51362 pistes +51363 pistez +51364 pistil +51365 piteux +51366 piton +51411 pitons +51412 pitre +51413 pivot +51414 pivote +51415 pivots +51416 pixel +51421 pixels +51422 pizza +51423 pizzas +51424 pj +51425 pk +51426 pl +51431 place +51432 places +51433 placet +51434 placez +51435 plage +51436 plagia +51441 plagie +51442 plaid +51443 plaids +51444 plaie +51445 plaies +51446 plaine +51451 plains +51452 plais +51453 plan +51454 plana +51455 plane +51456 planes +51461 planez +51462 plans +51463 plant +51464 plante +51465 plaqua +51466 plasma +51511 plat +51512 plate +51513 plates +51514 plats +51515 plein +51516 pleins +51521 pleur +51522 pleura +51523 pleure +51524 pleut +51525 pli +51526 plia +51531 pliage +51532 plie +51533 plier +51534 plies +51535 pliez +51536 pliiez +51541 plions +51542 plis +51543 pliure +51544 ploie +51545 plomb +51546 plombe +51551 plot +51552 plots +51553 ploya +51554 ployer +51555 ployez +51556 plu +51561 pluie +51562 plume +51563 plumer +51564 plumes +51565 plus +51566 plut +51611 pm +51612 pn +51613 pneu +51614 pneus +51615 po +51616 poche +51621 pocher +51622 poches +51623 podium +51624 pogrom +51625 poids +51626 poil +51631 poils +51632 poilu +51633 poilue +51634 poilus +51635 poing +51636 poings +51641 point +51642 pointa +51643 points +51644 pointu +51645 poire +51646 pois +51651 poisse +51652 poix +51653 poker +51654 pokers +51655 poli +51656 polie +51661 polio +51662 polios +51663 polir +51664 polis +51665 polit +51666 polka +52111 pollen +52112 polo +52113 polos +52114 pomme +52115 pommer +52116 pompa +52121 pompe +52122 pompez +52123 pompon +52124 ponce +52125 poncez +52126 pond +52131 ponde +52132 pondes +52133 pondre +52134 ponds +52135 pondu +52136 pondue +52141 poney +52142 pont +52143 ponte +52144 ponter +52145 ponts +52146 pool +52151 pools +52152 pope +52153 popes +52154 popote +52155 porc +52156 porcs +52161 pore +52162 pores +52163 porno +52164 pornos +52165 port +52166 porta +52211 porte +52212 porto +52213 ports +52214 posa +52215 pose +52216 poser +52221 poses +52222 posez +52223 posons +52224 posta +52225 postal +52226 poste +52231 postez +52232 pot +52233 potage +52234 pote +52235 potes +52236 potin +52241 potion +52242 pots +52243 pou +52244 pouce +52245 pouces +52246 poudra +52251 poudre +52252 pouf +52253 poufs +52254 poule +52255 poules +52256 poulet +52261 pouls +52262 poumon +52263 poupe +52264 poupes +52265 poupin +52266 poupon +52311 pour +52312 pourra +52313 pourri +52314 pourvu +52315 pousse +52316 poux +52321 pp +52322 ppp +52323 pppp +52324 pq +52325 pqr +52326 pr +52331 prend +52332 presse +52333 pria +52334 priait +52335 prie +52336 prier +52341 priera +52342 pries +52343 priez +52344 prima +52345 primat +52346 prime +52351 primer +52352 prions +52353 pris +52354 prisa +52355 prise +52356 prises +52361 prison +52362 prit +52363 priva +52364 prive +52365 priver +52366 prix +52411 probe +52412 proche +52413 profit +52414 proie +52415 proies +52416 projet +52421 promet +52422 promis +52423 promit +52424 promu +52425 promue +52426 promus +52431 promut +52432 pronom +52433 propre +52434 prose +52435 proue +52436 proues +52441 prude +52442 prune +52443 ps +52444 pt +52445 pu +52446 pua +52451 puait +52452 puant +52453 pub +52454 pubis +52455 public +52456 publie +52461 pubs +52462 puce +52463 puceau +52464 puces +52465 pue +52466 puent +52511 puer +52512 puera +52513 puis +52514 puisa +52515 puise +52516 puiser +52521 puises +52522 puisse +52523 puits +52524 pull +52525 pulls +52526 pulpe +52531 puma +52532 pumas +52533 punch +52534 puni +52535 punie +52536 punir +52541 punira +52542 punis +52543 punit +52544 pur +52545 pure +52546 purent +52551 pures +52552 purge +52553 purgea +52554 purger +52555 purin +52556 purs +52561 pus +52562 put +52563 putain +52564 putois +52565 puzzle +52566 pv +52611 pw +52612 px +52613 py +52614 pyjama +52615 pyrex +52616 pz +52621 q +52622 qa +52623 qb +52624 qc +52625 qd +52626 qe +52631 qf +52632 qg +52633 qh +52634 qi +52635 qj +52636 qk +52641 ql +52642 qm +52643 qn +52644 qo +52645 qp +52646 qq +52651 qqn +52652 qqq +52653 qqqq +52654 qqun +52655 qr +52656 qrs +52661 qs +52662 qt +52663 qu +52664 quai +52665 quais +52666 quand +53111 quant +53112 quart +53113 quarts +53114 quartz +53115 quasi +53116 quatre +53121 que +53122 quel +53123 quels +53124 queue +53125 queux +53126 qui +53131 quidam +53132 quille +53133 quinte +53134 quitus +53135 quo +53136 quoi +53141 quota +53142 quotas +53143 qv +53144 qw +53145 qx +53146 qy +53151 qz +53152 r +53153 ra +53154 rabane +53155 rabat +53156 rabbin +53161 rabiot +53162 rabot +53163 rabote +53164 race +53165 races +53166 racine +53211 racla +53212 racle +53213 raclez +53214 racola +53215 racole +53216 radar +53221 rade +53222 radeau +53223 rader +53224 rades +53225 radia +53226 radie +53231 radio +53232 radios +53233 radis +53234 radium +53235 radius +53236 radota +53241 radoub +53242 rafale +53243 raffut +53244 rafla +53245 rafle +53246 rafler +53251 rafles +53252 raflez +53253 rage +53254 ragea +53255 ragent +53256 rager +53261 ragera +53262 rages +53263 ragez +53264 ragot +53265 ragots +53266 raguer +53311 raid +53312 raide +53313 raidi +53314 raids +53315 raie +53316 raies +53321 rail +53322 railla +53323 raille +53324 rails +53325 rainer +53326 raire +53331 rama +53332 ramait +53333 rame +53334 ramena +53335 rament +53336 ramer +53341 rames +53342 rameur +53343 ramez +53344 rami +53345 ramis +53346 ramone +53351 ramons +53352 rampa +53353 rampe +53354 ramper +53355 rampez +53356 rance +53361 rances +53362 ranch +53363 ranci +53364 rancie +53365 rancir +53366 rancis +53411 rang +53412 range +53413 rangez +53414 rangs +53415 ranime +53416 rapace +53421 raphia +53422 rappel +53423 rapt +53424 rapts +53425 raquer +53426 rare +53431 rares +53432 ras +53433 rasa +53434 rasade +53435 rasage +53436 rasais +53441 rasant +53442 rase +53443 rasent +53444 raser +53445 rases +53446 rasez +53451 rasoir +53452 rasons +53453 rassir +53454 rassit +53455 rat +53456 rata +53461 ratage +53462 ratais +53463 ratant +53464 rate +53465 rater +53466 ratera +53511 rates +53512 ratez +53513 ratio +53514 ration +53515 raton +53516 ratons +53521 rats +53522 ravage +53523 ravale +53524 rave +53525 raves +53526 ravi +53531 ravie +53532 ravies +53533 ravin +53534 ravina +53535 ravir +53536 ravira +53541 ravis +53542 ravisa +53543 ravit +53544 ravoir +53545 raya +53546 rayait +53551 raye +53552 rayer +53553 rayes +53554 rayez +53555 rayon +53556 rayons +53561 raz +53562 razzia +53563 rb +53564 rc +53565 rd +53566 re +53611 rebat +53612 rebord +53613 rebut +53614 rebuta +53615 recel +53616 recoin +53621 record +53622 recoud +53623 recru +53624 recta +53625 recto +53626 rectum +53631 recuit +53632 recul +53633 redira +53634 redire +53635 redis +53636 redit +53641 redits +53642 redoux +53643 refera +53644 refit +53645 reflux +53646 refuge +53651 refus +53652 refuse +53653 reg +53654 regard +53655 regret +53656 regs +53661 rein +53662 reine +53663 reines +53664 reins +53665 rejet +53666 rejeta +54111 relaxa +54112 relent +54113 releva +54114 relia +54115 relie +54116 relira +54121 relis +54122 relise +54123 relit +54124 relu +54125 relue +54126 relui +54131 reluit +54132 relus +54133 relut +54134 remet +54135 remis +54136 remisa +54141 remit +54142 remous +54143 remua +54144 remue +54145 remuer +54146 renais +54151 renard +54152 rend +54153 rende +54154 rendes +54155 rendez +54156 rendit +54161 rendre +54162 rends +54163 rendu +54164 renia +54165 renie +54166 renies +54211 renne +54212 renom +54213 renoms +54214 renoua +54215 rente +54216 renter +54221 rentes +54222 rentra +54223 rentre +54224 renvoi +54225 repaie +54226 repars +54231 repas +54232 repaya +54233 repens +54234 repent +54235 repli +54236 repos +54241 reposa +54242 repose +54243 repris +54244 reprit +54245 repu +54246 repue +54251 repues +54252 repus +54253 ressac +54254 resta +54255 reste +54256 rester +54261 retard +54262 retenu +54263 revint +54264 revis +54265 revit +54266 revois +54311 revoit +54312 revu +54313 revue +54314 revus +54315 rf +54316 rg +54321 rh +54322 rhum +54323 rhume +54324 rhums +54325 ri +54326 riais +54331 riait +54332 riant +54333 riants +54334 ribler +54335 riche +54336 ricin +54341 rida +54342 ridant +54343 ride +54344 rider +54345 rides +54346 rie +54351 rien +54352 riens +54353 rient +54354 ries +54355 rieur +54356 riez +54361 rifle +54362 rifles +54363 rigide +54364 riiez +54365 rima +54366 rime +54411 riment +54412 rimer +54413 rimes +54414 rince +54415 rinces +54416 ring +54421 rings +54422 rions +54423 rioter +54424 riper +54425 rira +54426 rirai +54431 riras +54432 rire +54433 rirent +54434 rires +54435 rirez +54436 ririez +54441 rirons +54442 ris +54443 riser +54444 risqua +54445 risque +54446 rit +54451 rite +54452 rites +54453 rituel +54454 riva +54455 rivage +54456 rivais +54461 rival +54462 rivale +54463 rivant +54464 rive +54465 river +54466 rives +54511 rivet +54512 rivez +54513 riviez +54514 rivons +54515 rixe +54516 rixes +54521 riz +54522 rj +54523 rk +54524 rl +54525 rm +54526 rn +54531 ro +54532 robe +54533 rober +54534 robes +54535 robot +54536 roc +54541 roche +54542 roches +54543 rock +54544 rocker +54545 rocks +54546 rococo +54551 rocs +54552 roda +54553 rodage +54554 rodais +54555 rode +54556 roder +54561 rodes +54562 rodez +54563 rogna +54564 rogne +54565 rogner +54566 rognez +54611 rognon +54612 rogue +54613 roi +54614 rois +54615 roll +54616 romain +54621 roman +54622 rompe +54623 rompes +54624 romps +54625 rompt +54626 rompu +54631 rompue +54632 ronce +54633 ronces +54634 rond +54635 ronde +54636 rondin +54641 ronds +54642 ronfla +54643 ronge +54644 rongea +54645 roque +54646 rosace +54651 rosbif +54652 rose +54653 roser +54654 roses +54655 rosi +54656 rosie +54661 rosir +54662 rosis +54663 rosit +54664 rosse +54665 rosses +54666 rot +55111 roter +55112 rotin +55113 rotins +55114 rotor +55115 rots +55116 rotule +55121 roue +55122 rouer +55123 roues +55124 rouge +55125 rouget +55126 rougi +55131 rougie +55132 rougir +55133 rouir +55134 roula +55135 roule +55136 rouler +55141 roules +55142 roulez +55143 roulis +55144 round +55145 rounds +55146 rousse +55151 routa +55152 route +55153 routes +55154 roux +55155 royal +55156 royale +55161 royaux +55162 rp +55163 rq +55164 rr +55165 rrr +55166 rrrr +55211 rs +55212 rst +55213 rt +55214 ru +55215 rua +55216 ruade +55221 ruais +55222 ruait +55223 ruant +55224 ruban +55225 rubis +55226 ruche +55231 rude +55232 rudes +55233 rudoie +55234 rudoya +55235 rue +55236 ruent +55241 ruer +55242 ruera +55243 rueras +55244 ruerez +55245 rues +55246 ruez +55251 rugby +55252 rugbys +55253 rugi +55254 rugir +55255 rugira +55256 rugis +55261 rugit +55262 ruiez +55263 ruiler +55264 ruina +55265 ruine +55266 ruinez +55311 ruions +55312 rumina +55313 rumine +55314 ruons +55315 rural +55316 rusa +55321 ruse +55322 ruser +55323 rusera +55324 ruses +55325 rusez +55326 rush +55331 rushs +55332 rusiez +55333 russe +55334 russes +55335 rut +55336 rutila +55341 ruts +55342 rv +55343 rw +55344 rx +55345 ry +55346 rythme +55351 rz +55352 s +55353 sa +55354 sabir +55355 sabirs +55356 sable +55361 sabot +55362 sabote +55363 sabra +55364 sabre +55365 sabrer +55366 sabres +55411 sabrez +55412 sac +55413 sache +55414 saches +55415 sacre +55416 sacrer +55421 sacres +55422 sacs +55423 saga +55424 sagas +55425 sage +55426 sages +55431 saille +55432 sailli +55433 sain +55434 saine +55435 saines +55436 sains +55441 saint +55442 sainte +55443 sais +55444 saisi +55445 saisie +55446 saisis +55451 sait +55452 sala +55453 salait +55454 sale +55455 saler +55456 sales +55461 salez +55462 sali +55463 salie +55464 salin +55465 saline +55466 salir +55511 salis +55512 salit +55513 salive +55514 salle +55515 salmis +55516 saloir +55521 salon +55522 salons +55523 saloon +55524 salua +55525 salue +55526 saluez +55531 salut +55532 salve +55533 samba +55534 samedi +55535 sana +55536 sanas +55541 sang +55542 sangs +55543 sans +55544 santon +55545 saoul +55546 saoule +55551 sapa +55552 sapait +55553 sapant +55554 sape +55555 saper +55556 sapera +55561 sapes +55562 sapeur +55563 sapin +55564 saquer +55565 sari +55566 saris +55611 sarrau +55612 sas +55613 satin +55614 satins +55615 satire +55616 satura +55621 sauce +55622 sauces +55623 sauf +55624 saufs +55625 sauge +55626 sauges +55631 saule +55632 saules +55633 sauna +55634 sauner +55635 saur +55636 saura +55641 saurs +55642 saut +55643 sauta +55644 saute +55645 sauts +55646 sauva +55651 sauve +55652 savait +55653 savant +55654 savez +55655 saviez +55656 savon +55661 savons +55662 saxo +55663 saxon +55664 saxos +55665 sb +55666 sbire +56111 sc +56112 scalp +56113 scanda +56114 scande +56115 sceau +56116 scella +56121 scia +56122 sciant +56123 scie +56124 scier +56125 sciera +56126 scies +56131 sciez +56132 sciiez +56133 scion +56134 scions +56135 scoop +56136 score +56141 scorie +56142 scout +56143 scouts +56144 scruta +56145 sd +56146 se +56151 seau +56152 seaux +56153 sec +56154 secs +56155 secte +56156 seiche +56161 sein +56162 seing +56163 seins +56164 seize +56165 sel +56166 self +56211 selfs +56212 selle +56213 seller +56214 selon +56215 sels +56216 sema +56221 semais +56222 semble +56223 semer +56224 semez +56225 semis +56226 semoir +56231 sens +56232 sent +56233 sente +56234 senti +56235 sentir +56236 sentis +56241 seoir +56242 sept +56243 sera +56244 serai +56245 seras +56246 serez +56251 serf +56252 serfs +56253 serge +56254 serin +56255 serons +56256 seront +56261 serpe +56262 serra +56263 serre +56264 serrer +56265 sers +56266 sert +56311 serti +56312 sertie +56313 serval +56314 serve +56315 servi +56316 servit +56321 ses +56322 set +56323 sets +56324 setter +56325 seuil +56326 seuils +56331 seul +56332 seule +56333 seules +56334 seuls +56335 sevra +56336 sevrer +56341 sevrez +56342 sexe +56343 sexes +56344 sexy +56345 seyant +56346 sf +56351 sg +56352 sh +56353 shah +56354 shahs +56355 shako +56356 shoot +56361 shoots +56362 short +56363 shorts +56364 show +56365 shows +56366 si +56411 sic +56412 sied +56413 sien +56414 sienne +56415 siens +56416 sieste +56421 sieur +56422 siffla +56423 sigle +56424 signa +56425 signal +56426 signe +56431 signer +56432 signes +56433 signet +56434 signez +56435 silex +56436 sillon +56441 silo +56442 silos +56443 simoun +56444 singe +56445 singer +56446 sinon +56451 sinus +56452 siphon +56453 sire +56454 sirop +56455 sis +56456 sisal +56461 sise +56462 sises +56463 site +56464 sites +56465 situa +56466 situe +56511 situer +56512 situes +56513 situez +56514 six +56515 sj +56516 sk +56521 skate +56522 ski +56523 skia +56524 skiant +56525 skie +56526 skient +56531 skier +56532 skiera +56533 skies +56534 skieur +56535 skiez +56536 skiff +56541 skiffs +56542 skions +56543 skis +56544 sl +56545 slave +56546 slip +56551 slips +56552 slogan +56553 slow +56554 slows +56555 sm +56556 smash +56561 sn +56562 snack +56563 snob +56564 snobs +56565 so +56566 sobre +56611 sobres +56612 soc +56613 social +56614 socle +56615 socles +56616 socs +56621 soda +56622 sodas +56623 sodium +56624 soeur +56625 soeurs +56626 sofa +56631 sofas +56632 soi +56633 soie +56634 soient +56635 soies +56636 soif +56641 soifs +56642 soigna +56643 soigne +56644 soin +56645 soins +56646 soir +56651 soirs +56652 sois +56653 soit +56654 soja +56655 sojas +56656 sol +56661 solda +56662 solde +56663 soldez +56664 sole +56665 solen +56666 soles +61111 soli +61112 solo +61113 solos +61114 sols +61115 sombra +61116 sombre +61121 somma +61122 somme +61123 sommes +61124 sommet +61125 sommez +61126 son +61131 sonar +61132 sonate +61133 sonda +61134 sonde +61135 sondes +61136 songe +61141 songea +61142 songer +61143 songez +61144 sonna +61145 sonne +61146 sonner +61151 sonnet +61152 sono +61153 sonos +61154 sons +61155 sont +61156 sorbe +61161 sorbes +61162 sors +61163 sort +61164 sorte +61165 sorti +61166 sortie +61211 sortis +61212 sortit +61213 sorts +61214 sosie +61215 sot +61216 sots +61221 sotte +61222 sottes +61223 sou +61224 souci +61225 soucia +61226 soucie +61231 soucis +61232 souda +61233 soude +61234 souder +61235 soudes +61236 souk +61241 souks +61242 soulte +61243 soumet +61244 soumit +61245 soupa +61246 soupe +61251 souper +61252 soupes +61253 soupez +61254 source +61255 sourd +61256 sourde +61261 sourds +61262 souri +61263 souris +61264 sous +61265 soute +61266 soviet +61311 soya +61312 soyas +61313 soyeux +61314 soyez +61315 sp +61316 spasme +61321 sphinx +61322 spire +61323 spires +61324 sport +61325 sports +61326 spot +61331 spots +61332 sq +61333 square +61334 squaw +61335 sr +61336 ss +61341 sss +61342 ssss +61343 st +61344 stable +61345 stade +61346 stades +61351 staff +61352 stage +61353 stages +61354 stagna +61355 stagne +61356 stand +61361 stands +61362 star +61363 stars +61364 statif +61365 statu +61366 statua +61411 statut +61412 steak +61413 steaks +61414 stem +61415 stemm +61416 stemms +61421 stems +61422 stick +61423 stock +61424 stocka +61425 stocke +61426 stop +61431 stoppe +61432 stops +61433 store +61434 stores +61435 strate +61436 strie +61441 stu +61442 stuc +61443 stucs +61444 style +61445 styles +61446 stylet +61451 stylo +61452 su +61453 sua +61454 suais +61455 suait +61456 suant +61461 suave +61462 suaves +61463 subi +61464 subie +61465 subies +61466 subir +61511 subis +61512 subit +61513 subits +61514 suc +61515 suce +61516 sucent +61521 sucer +61522 sucera +61523 suces +61524 sucez +61525 sucra +61526 sucre +61531 sucres +61532 sucs +61533 sud +61534 sue +61535 suent +61536 suer +61541 suera +61542 suerai +61543 sueras +61544 sues +61545 sueur +61546 suez +61551 suffi +61552 suffit +61553 suie +61554 suiez +61555 suif +61556 suint +61561 suinta +61562 suinte +61563 suis +61564 suisse +61565 suit +61566 suite +61611 suive +61612 suives +61613 suivi +61614 suivie +61615 suivis +61616 suivit +61621 suivre +61622 sujet +61623 sujets +61624 sulky +61625 sulkys +61626 sultan +61631 suons +61632 super +61633 supin +61634 sur +61635 sure +61636 surent +61641 sures +61642 surf +61643 surfa +61644 surfai +61645 surfas +61646 surfe +61651 surfil +61652 surfs +61653 surgi +61654 surgie +61655 surir +61656 surjet +61661 surs +61662 sursis +61663 survis +61664 survit +61665 survol +61666 sus +62111 sut +62112 sv +62113 sw +62114 swap +62115 swaps +62116 swing +62121 swings +62122 sx +62123 sy +62124 sympa +62125 sympas +62126 syndic +62131 synode +62132 syrien +62133 sz +62134 t +62135 ta +62136 tabac +62141 tabacs +62142 tabla +62143 table +62144 tabler +62145 tabou +62146 tac +62151 tacha +62152 tache +62153 tachez +62154 tact +62155 tacts +62156 taie +62161 taies +62162 tailla +62163 taille +62164 tain +62165 taira +62166 tairas +62211 taire +62212 tairez +62213 tais +62214 taise +62215 tait +62216 talc +62221 talcs +62222 taler +62223 talon +62224 talons +62225 talus +62226 tamis +62231 tan +62232 tandem +62233 tandis +62234 tango +62235 tangua +62236 tangue +62241 tanin +62242 tank +62243 tanks +62244 tant +62245 tante +62246 tantes +62251 taon +62252 taons +62253 tapa +62254 tapais +62255 tapait +62256 tapant +62261 tape +62262 taper +62263 tapes +62264 tapeur +62265 tapez +62266 tapi +62311 tapie +62312 tapies +62313 tapir +62314 tapira +62315 tapirs +62316 tapis +62321 tapit +62322 tapons +62323 tapota +62324 taquet +62325 taquin +62326 tard +62331 tarda +62332 tarde +62333 tardez +62334 tardif +62335 tare +62336 tarer +62341 tares +62342 targua +62343 tari +62344 tarie +62345 taries +62346 tarif +62351 tarifa +62352 tarife +62353 tarir +62354 taris +62355 tarit +62356 tarot +62361 tarots +62362 tarte +62363 tartes +62364 tartir +62365 tas +62366 tassa +62411 tasse +62412 tasser +62413 tasses +62414 tassez +62415 taule +62416 taupe +62421 taupes +62422 taurin +62423 taux +62424 taxa +62425 taxant +62426 taxe +62431 taxent +62432 taxer +62433 taxes +62434 taxez +62435 taxi +62436 taxis +62441 tb +62442 tc +62443 td +62444 te +62445 teck +62446 teins +62451 teint +62452 tek +62453 teks +62454 tel +62455 telle +62456 tels +62461 tempe +62462 tempes +62463 tempo +62464 tempos +62465 temps +62466 tenais +62511 tenait +62512 tend +62513 tende +62514 tendez +62515 tends +62516 tendu +62521 tendue +62522 teneur +62523 tenez +62524 teniez +62525 tenir +62526 tenon +62531 tenons +62532 tenta +62533 tente +62534 tenter +62535 tentes +62536 tentez +62541 tenu +62542 tenue +62543 tenues +62544 tenus +62545 ter +62546 terme +62551 termes +62552 terne +62553 terni +62554 ternis +62555 ternit +62556 terra +62561 terre +62562 terrer +62563 terri +62564 terril +62565 tertre +62566 tes +62611 test +62612 testa +62613 teste +62614 testes +62615 testez +62616 tests +62621 texan +62622 texte +62623 textes +62624 tf +62625 tg +62626 th +62631 thon +62632 thons +62633 thuya +62634 thuyas +62635 thym +62636 ti +62641 tiare +62642 tibia +62643 tic +62644 tics +62645 tien +62646 tienne +62651 tiens +62652 tient +62653 tierce +62654 tiers +62655 tige +62656 tiges +62661 tigre +62662 tigres +62663 tilde +62664 tildes +62665 tiller +62666 tilt +63111 tilts +63112 timbre +63113 timide +63114 timon +63115 tint +63116 tinta +63121 tinte +63122 tique +63123 tiques +63124 tir +63125 tira +63126 tirade +63131 tirait +63132 tirant +63133 tire +63134 tirent +63135 tirer +63136 tirera +63141 tires +63142 tiret +63143 tirets +63144 tirez +63145 tiroir +63146 tirs +63151 tisane +63152 tison +63153 tisons +63154 tissa +63155 tisse +63156 tisses +63161 tissez +63162 tissu +63163 titan +63164 titane +63165 titans +63166 titra +63211 titre +63212 titrer +63213 tj +63214 tk +63215 tl +63216 tm +63221 tn +63222 to +63223 toast +63224 toc +63225 tocs +63226 toge +63231 toges +63232 toi +63233 toile +63234 toiles +63235 toisa +63236 toise +63241 toiser +63242 toises +63243 toisez +63244 toison +63245 toit +63246 toits +63251 tomba +63252 tombe +63253 tombes +63254 tome +63255 tomes +63256 tomme +63261 ton +63262 tonal +63263 tonals +63264 tond +63265 tonde +63266 tondre +63311 tonds +63312 tondu +63313 tondue +63314 tonna +63315 tonne +63316 tonner +63321 tonnes +63322 tons +63323 tonte +63324 tonus +63325 top +63326 topaze +63331 toper +63332 tops +63333 toque +63334 toquer +63335 tord +63336 torde +63341 tordre +63342 tords +63343 tordu +63344 tordus +63345 torero +63346 toril +63351 torils +63352 toron +63353 torons +63354 tors +63355 torse +63356 torses +63361 tort +63362 torts +63363 torve +63364 tosser +63365 total +63366 totale +63411 totaux +63412 totem +63413 touer +63414 toupet +63415 toupie +63416 tour +63421 tours +63422 tous +63423 tout +63424 toute +63425 toutes +63426 toux +63431 toxine +63432 tp +63433 tq +63434 tr +63435 trac +63436 trace +63441 tract +63442 tracta +63443 tracte +63444 trahi +63445 trahie +63446 trahir +63451 traie +63452 train +63453 trais +63454 trait +63455 traita +63456 traits +63461 tram +63462 trama +63463 trame +63464 trames +63465 trams +63466 transi +63511 trapu +63512 traqua +63513 trauma +63514 treize +63515 trempe +63516 tresse +63521 treuil +63522 tri +63523 tria +63524 triade +63525 triage +63526 triais +63531 tribu +63532 tribun +63533 triche +63534 tricot +63535 trie +63536 trient +63541 trier +63542 triera +63543 tries +63544 triez +63545 triiez +63546 trille +63551 trio +63552 trios +63553 tripe +63554 tris +63555 triste +63556 troc +63561 trocs +63562 trois +63563 troll +63564 trombe +63565 tronc +63566 trop +63611 trope +63612 troqua +63613 troque +63614 trot +63615 trots +63616 trotta +63621 trou +63622 troua +63623 troue +63624 troues +63625 trous +63626 trouve +63631 truand +63632 truc +63633 trucs +63634 truffa +63635 truie +63636 truies +63641 truite +63642 trust +63643 truste +63644 ts +63645 tsar +63646 tsars +63651 tt +63652 ttt +63653 tttt +63654 tu +63655 tua +63656 tuais +63661 tuait +63662 tuant +63663 tuba +63664 tubas +63665 tube +63666 tuber +64111 tubes +64112 tue +64113 tuent +64114 tuer +64115 tuera +64116 tueras +64121 tuerez +64122 tuerie +64123 tues +64124 tueur +64125 tueurs +64126 tueuse +64131 tuez +64132 tuf +64133 tufs +64134 tuiez +64135 tuile +64136 tuiles +64141 tulle +64142 tuner +64143 tuners +64144 tuons +64145 turban +64146 turc +64151 turcs +64152 turent +64153 turf +64154 turfs +64155 tus +64156 tussor +64161 tut +64162 tuv +64163 tuyau +64164 tuyaux +64165 tv +64166 tw +64211 tweed +64212 tweeds +64213 tx +64214 ty +64215 type +64216 typer +64221 types +64222 typhon +64223 typo +64224 typon +64225 typons +64226 typos +64231 tyran +64232 tyrans +64233 tz +64234 u +64235 ua +64236 ub +64241 ubac +64242 ubacs +64243 uc +64244 ud +64245 ue +64246 uf +64251 ug +64252 uh +64253 ui +64254 uj +64255 uk +64256 ukase +64261 ukases +64262 ul +64263 um +64264 un +64265 une +64266 unes +64311 uni +64312 unie +64313 unies +64314 unifia +64315 unifie +64316 union +64321 unique +64322 unir +64323 unira +64324 unirez +64325 unis +64326 unit +64331 uns +64332 uo +64333 up +64334 uq +64335 ur +64336 urge +64341 urgea +64342 urger +64343 urine +64344 urne +64345 urnes +64346 us +64351 usa +64352 usage +64353 usager +64354 usais +64355 usait +64356 usant +64361 usante +64362 usants +64363 use +64364 usent +64365 user +64366 usera +64411 userai +64412 useras +64413 userez +64414 uses +64415 usez +64416 usiez +64421 usina +64422 usine +64423 usiner +64424 usons +64425 usuel +64426 usure +64431 usurpa +64432 ut +64433 utile +64434 utiles +64435 uu +64436 uuu +64441 uuuu +64442 uv +64443 uvw +64444 uw +64445 ux +64446 uy +64451 uz +64452 v +64453 va +64454 vacant +64455 vache +64456 vagin +64461 vagins +64462 vagir +64463 vague +64464 vagues +64465 vaille +64466 vain +64511 vainc +64512 vaincs +64513 vaine +64514 vaines +64515 vains +64516 vais +64521 val +64522 valais +64523 valant +64524 valent +64525 valet +64526 valeur +64531 valez +64532 valgus +64533 vallon +64534 valoir +64535 valons +64536 vals +64541 valsa +64542 valse +64543 valser +64544 valsez +64545 valu +64546 value +64551 valus +64552 valut +64553 valve +64554 vamp +64555 vamper +64556 vamps +64561 van +64562 vanne +64563 vanner +64564 vans +64565 vanta +64566 vante +64611 vanter +64612 vantes +64613 vapeur +64614 vaqua +64615 vaque +64616 vaquer +64621 varan +64622 varech +64623 varia +64624 varie +64625 varus +64626 vas +64631 vase +64632 vases +64633 vaseux +64634 vassal +64635 vaste +64636 vastes +64641 vaut +64642 vaux +64643 vb +64644 vc +64645 vd +64646 ve +64651 veau +64652 veaux +64653 veine +64654 veiner +64655 velu +64656 velue +64661 velus +64662 vend +64663 vende +64664 vendit +64665 vendra +64666 vendre +65111 vends +65112 vendu +65113 veneur +65114 venez +65115 venge +65116 vengea +65121 venger +65122 venges +65123 vengez +65124 venin +65125 venir +65126 venons +65131 vent +65132 venta +65133 vente +65134 ventru +65135 vents +65136 venu +65141 venue +65142 venus +65143 ver +65144 verbe +65145 verbes +65146 verdi +65151 verdir +65152 verdit +65153 verge +65154 verger +65155 verni +65156 vernie +65161 vernis +65162 verra +65163 verre +65164 verres +65165 verrez +65166 verrou +65211 vers +65212 versa +65213 verse +65214 verser +65215 verses +65216 versez +65221 verso +65222 versos +65223 vert +65224 verte +65225 vertes +65226 verts +65231 vertu +65232 verve +65233 vessie +65234 veste +65235 veto +65236 veuf +65241 veufs +65242 veule +65243 veut +65244 veuve +65245 veux +65246 vexa +65251 vexais +65252 vexant +65253 vexe +65254 vexer +65255 vexes +65256 vexez +65261 vexiez +65262 vf +65263 vg +65264 vh +65265 vi +65266 via +65311 viable +65312 viager +65313 viande +65314 vibra +65315 vibre +65316 vice +65321 vices +65322 vichy +65323 vicia +65324 vicie +65325 vida +65326 vidage +65331 vidais +65332 vidait +65333 vidant +65334 vide +65335 vider +65336 videra +65341 vides +65342 videz +65343 vie +65344 vieil +65345 vielle +65346 vienne +65351 viens +65352 vient +65353 vies +65354 vieux +65355 vif +65356 vifs +65361 vigie +65362 vigies +65363 vigile +65364 vigne +65365 vil +65366 vile +65411 viles +65412 villa +65413 villas +65414 ville +65415 vils +65416 vin +65421 viner +65422 vineux +65423 vingt +65424 vins +65425 vint +65426 vinyle +65431 viol +65432 viola +65433 viole +65434 violes +65435 violet +65436 violon +65441 viols +65442 vira +65443 virago +65444 virais +65445 viral +65446 virant +65451 vire +65452 virer +65453 virera +65454 vires +65455 virez +65456 viriez +65461 viril +65462 virons +65463 virus +65464 vis +65465 visa +65466 visage +65511 visais +65512 visait +65513 visant +65514 visas +65515 vise +65516 viser +65521 visera +65522 vises +65523 visez +65524 visiez +65525 vision +65526 visita +65531 vison +65532 visons +65533 vissa +65534 visse +65535 visser +65536 vissez +65541 visuel +65542 vit +65543 vitae +65544 vital +65545 vite +65546 vitre +65551 vitrer +65552 vitres +65553 vivace +65554 vivat +65555 vive +65556 vivent +65561 vives +65562 vivez +65563 vivier +65564 viviez +65565 vivote +65566 vivra +65611 vivrai +65612 vivras +65613 vivre +65614 vj +65615 vk +65616 vl +65621 vm +65622 vn +65623 vo +65624 vocal +65625 vocale +65626 vodka +65631 vodkas +65632 voeu +65633 voeux +65634 vogua +65635 vogue +65636 voguer +65641 vogues +65642 voici +65643 voie +65644 voies +65645 voila +65646 voile +65651 voir +65652 voire +65653 voirie +65654 vois +65655 voit +65656 voix +65661 vol +65662 vola +65663 volait +65664 vole +65665 volent +65666 voler +66111 volera +66112 voles +66113 volet +66114 volets +66115 voleur +66116 volez +66121 vols +66122 volt +66123 volts +66124 volute +66125 vomi +66126 vomie +66131 vomies +66132 vomir +66133 vomis +66134 vomit +66135 vont +66136 vos +66141 vota +66142 votais +66143 votant +66144 vote +66145 votent +66146 voter +66151 votera +66152 votes +66153 votez +66154 votons +66155 votre +66156 voua +66161 vouais +66162 vouait +66163 voue +66164 vouent +66165 vouer +66166 voues +66211 vouez +66212 vouiez +66213 voulu +66214 voulue +66215 vous +66216 voyais +66221 voyeur +66222 voyez +66223 voyiez +66224 voyons +66225 voyou +66226 vp +66231 vq +66232 vr +66233 vrac +66234 vrai +66235 vraie +66236 vrais +66241 vrille +66242 vs +66243 vt +66244 vu +66245 vue +66246 vues +66251 vulve +66252 vus +66253 vv +66254 vvv +66255 vvvv +66256 vw +66261 vwx +66262 vx +66263 vy +66264 vz +66265 w +66266 wa +66311 wagon +66312 waters +66313 watt +66314 watts +66315 wb +66316 wc +66321 wd +66322 we +66323 wf +66324 wg +66325 wh +66326 wi +66331 wj +66332 wk +66333 wl +66334 wm +66335 wn +66336 wo +66341 wp +66342 wq +66343 wr +66344 ws +66345 wt +66346 wu +66351 wv +66352 ww +66353 www +66354 wwww +66355 wx +66356 wxy +66361 wy +66362 wz +66363 x +66364 xa +66365 xb +66366 xc +66411 xd +66412 xe +66413 xf +66414 xg +66415 xh +66416 xi +66421 xj +66422 xk +66423 xl +66424 xm +66425 xn +66426 xo +66431 xp +66432 xq +66433 xr +66434 xs +66435 xt +66436 xu +66441 xv +66442 xw +66443 xx +66444 xxx +66445 xxxx +66446 xy +66451 xyz +66452 xz +66453 y +66454 ya +66455 yacht +66456 yack +66461 yacks +66462 yak +66463 yaks +66464 yard +66465 yards +66466 yb +66511 yc +66512 yd +66513 ye +66514 yeux +66515 yf +66516 yg +66521 yh +66522 yi +66523 yj +66524 yk +66525 yl +66526 ym +66531 yn +66532 yo +66533 yoga +66534 yp +66535 yq +66536 yr +66541 ys +66542 yt +66543 yu +66544 yv +66545 yw +66546 yx +66551 yy +66552 yyy +66553 yyyy +66554 yz +66555 z +66556 za +66561 zb +66562 zc +66563 zd +66564 ze +66565 zen +66566 zeste +66611 zester +66612 zestes +66613 zf +66614 zg +66615 zh +66616 zi +66621 zinc +66622 zincs +66623 zipper +66624 zj +66625 zk +66626 zl +66631 zm +66632 zn +66633 zo +66634 zona +66635 zonas +66636 zone +66641 zones +66642 zoo +66643 zoom +66644 zooms +66645 zoos +66646 zp +66651 zq +66652 zr +66653 zs +66654 zt +66655 zu +66656 zv +66661 zw +66662 zx +66663 zy +66664 zz +66665 zzz +66666 zzzz diff --git a/zen/tools/diceware.sh b/zen/tools/diceware.sh new file mode 100755 index 0000000..700c971 --- /dev/null +++ b/zen/tools/diceware.sh @@ -0,0 +1,33 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.18 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +# \\/// +# qo-op +############# +########################## +####################################### +#################################################### +######################################################################## + +MOTS=$(echo "$1" | grep -E "^\-?[0-9]+$") +# Default is 6 words passphrase +if [[ "$MOTS" == "" ]]; then MOTS=6; fi +WORDCOUNT=${1-$MOTS} +# Download the wordlist +# wget -nc -O ~/.diceware-wordlist http://world.std.com/%7Ereinhold/diceware.wordlist.asc 2> /dev/null +# print a list of the diceware words +cat $MY_PATH/diceware-wordlist.txt | \ +awk '/[1-6][1-6][1-6][1-6][1-6]/{ print $2 }' | \ +# randomize the list order +shuf --random-source=/dev/urandom | \ +# pick the first n words +head -n ${WORDCOUNT} | \ +# pretty print +tr '\n' ' ' +echo diff --git a/zen/tools/dunikey2secret.sh b/zen/tools/dunikey2secret.sh new file mode 100755 index 0000000..0663683 --- /dev/null +++ b/zen/tools/dunikey2secret.sh @@ -0,0 +1,42 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 1.0 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# This script convert secret.dunikey into ~/.ssb/secret.ssb +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +# \\/// +# qo-op +############# +DUNIKEYFILE="$1" +[[ ! ${DUNIKEYFILE} ]] && DUNIKEYFILE="/tmp/secret.dunikey" +[[ ! ${DUNIKEYFILE} ]] && DUNIKEYFILE="~/.ssb/secret.dunikey" +[[ ! -f ${DUNIKEYFILE} ]] && echo "ERROR secret.dunikey unfound" && exit 1 + +pub=$(cat ${DUNIKEYFILE} | grep "pub" | cut -d ' ' -f 2) +priv=$(cat ${DUNIKEYFILE} | grep "sec" | cut -d ' ' -f 2) +ssbpub=$(echo $pub | base58 -d | base64) +ssbpriv=$(echo $priv | base58 -d | base64 | tr -d "\n") + +cat > /tmp/secret.ssb <&1 )"` + n=$node + if [[ "$cur" != *issuersFrameVar* ]] + then + # It failed in https, maybe try http? + cur=`echo "$( { curl -m $httpTimeout http://$node/blockchain/current; } 2>&1 )"` + if [[ "$cur" == *issuersFrameVar* ]] + then + # Indicate that the node is http + n="$n-(http)" + fi + fi + if [[ "$cur" != *issuersFrameVar* ]] + then + # The node didn't respond on time + cur="ERROR" + else + # The node did respond - grab the block number and hash of the block as key + cur="`echo "$cur"|grep '^ "number": '|awk '{print $2}'|awk -F, '{print $1}'`-`echo "$cur"|grep '^ "hash": '|awk '{print $2}'|awk '{print substr($1,2,13)}'`" + fi + if [[ $watched =~ .*#$node#.* ]] + then + # The node is a watched node, add some bold + n="\e[1m$n\e[0m" + fi + # Put the result into the file + echo "$cur $n">$outfile + # Notify that we're done here + touch $outfile.done +} + +# Temp dir where results are stored +rm -Rf /tmp/zen/gnodewatch +DIR=/tmp/zen/gnodewatch +export DIR +mkdir -p $DIR/chains +# TODO: REMOVE 777 PATCH, ACTIVATE ramdisk +# sudo mkdir /mnt/ramdisk +# sudo mount -t tmpfs -o size=50m tmpfs /mnt/ramdisk +chmod -R 777 /tmp/zen/ + +# KEEP /tmp/zen/current.duniter for 5 mn +find /tmp/zen/ -mmin +5 -type f -name "current.duniter" -exec rm -f '{}' \; +[[ -f /tmp/zen/current.duniter ]] && cat /tmp/zen/current.duniter && exit 0 + +##### $DIR/duniter_nodes.txt REFRESH after 20 minutes ##### +find $DIR/ -mmin +20 -type f -name "duniter_*" -exec rm -f '{}' \; +if [[ ! -f $DIR/duniter_nodes.txt ]]; then + # Get New BMAS known Nodes list from shuffle one $DIR/good.nodes.txt + [[ -f $DIR/good.nodes.txt ]] && DUNITER=$(shuf -n 1 $DIR/good.nodes.txt) || DUNITER="duniter-g1.p2p.legal:443" + curl -s https://$DUNITER/network/peers | jq '.peers[] | .endpoints' | grep BMAS | awk '{print $2,$3}' | sed s/\"//g | sed s/\,//g | sed s/\ /:/g | sort -u > $DIR/duniter_nodes.txt +fi + +# Grab the nodes we are actively watching - they will be in bold in the final output +watched=`grep -v "#" $DIR/duniter_nodes.txt|egrep "\!$"|awk '{print "#" $1 "#"}'` +# All nodes we are watching +nodes=`grep -v "#" $DIR/duniter_nodes.txt|awk '{print $1}'` +# The index to generate separate file names +index=0 +# Wipe out the output directory +rm $DIR/*out $DIR/*done $DIR/chains/* $DIR/NODE.* 2>/dev/null + +# Query all nodes in parallel +for node in $nodes +do + checkonenode $node "$watched" $DIR/$index.out & + ((index++)) +done + +# Wait a little for the first files to be created +sleep 1s +# Wait for all the threads to report they are done +while [ `ls $DIR/*done|wc -l` -lt $index ] +do + sleep 1s +done + +# Grab all results +curs=`cat $DIR/*out|sort` +# Extract all forks, excluding all errors +chains="`echo "$curs"|grep -v ERROR|awk '{print $1}'|sort -r|uniq`" + +# Count the number of chains and output most recent consensus to "good.nodes.txt" +nb=0 +for chain in $chains +do + echo "$curs" | egrep "^$chain " | awk '{print $2}' >> $DIR/chains/$nb; + ((nb++)) +done + +longchain=$(ls -S $DIR/chains/ | head -n 1) +# WRITE OUT shuffle Duniter Node Sync with longest chain +cp $DIR/chains/$longchain $DIR/good.nodes.txt + +## TEST if server is really running Duniter +Dtest=""; IDtest=""; lastresult=""; loop=0 +while [[ $Dtest != "duniter" ]]; do + while [[ $lastresult == $result && $loop -lt 7 ]]; do result=$(shuf -n 1 $DIR/good.nodes.txt); ((loop++)); done + lastresult=$result + Dtest=$(curl -s https://$lastresult | jq -r .duniter.software) + ## CHECK if server is not too slow + [[ $Dtest == "duniter" ]] && IDtest=$(silkaj -p $lastresult id Fred) + [[ $IDtest == "" ]] && Dtest="" + [[ $loop -eq 8 ]] && result="duniter-g1.p2p.legal:443" && break + ((loop++)) +done + + +echo "$result" > /tmp/zen/current.duniter + +echo $result diff --git a/zen/tools/emoji.pl b/zen/tools/emoji.pl new file mode 100644 index 0000000..9848ed7 --- /dev/null +++ b/zen/tools/emoji.pl @@ -0,0 +1,29 @@ +# Oona Räisänen 2013 +# http://windytan.com + +# ssh-keygen -l -f ~/.ssh/id_rsa.pub | perl emoji.pl + +@emoji = qw( 🌀 🌂 🌅 🌈 🌙 🌞 🌟 🌠 🌰 🌱 🌲 🌳 🌴 🌵 🌷 🌸 + 🌹 🌺 🌻 🌼 🌽 🌾 🌿 🍀 🍁 🍂 🍃 🍄 🍅 🍆 🍇 🍈 + 🍉 🍊 🍋 🍌 🍍 🍎 🍏 🍐 🍑 🍒 🍓 🍔 🍕 🍖 🍗 🍘 + 🍜 🍝 🍞 🍟 🍠 🍡 🍢 🍣 🍤 🍥 🍦 🍧 🍨 🍩 🍪 🍫 + 🍬 🍭 🍮 🍯 🍰 🍱 🍲 🍳 🍴 🍵 🍶 🍷 🍸 🍹 🍺 🍻 + 🍼 🎀 🎁 🎂 🎃 🎄 🎅 🎈 🎉 🎊 🎋 🎌 🎍 🎎 🎏 🎒 + 🎓 🎠 🎡 🎢 🎣 🎤 🎥 🎦 🎧 🎨 🎩 🎪 🎫 🎬 🎭 🎮 + 🎯 🎰 🎱 🎲 🎳 🎴 🎵 🎷 🎸 🎹 🎺 🎻 🎽 🎾 🎿 🏀 + 🏁 🏂 🏃 🏄 🏆 🏇 🏈 🏉 🏊 🐀 🐁 🐂 🐃 🐄 🐅 🐆 + 🐇 🐈 🐉 🐊 🐋 🐌 🐍 🐎 🐏 🐐 🐑 🐒 🐓 🐔 🐕 🐖 + 🐗 🐘 🐙 🐚 🐛 🐜 🐝 🐞 🐟 🐠 🐡 🐢 🐣 🐤 🐥 🐦 + 🐧 🐨 🐩 🐪 🐫 🐬 🐭 🐮 🐯 🐰 🐱 🐲 🐳 🐴 🐵 🐶 + 🐷 🐸 🐹 🐺 🐻 🐼 🐽 🐾 👀 👂 👃 👄 👅 👆 👇 👈 + 👉 👊 👋 👌 👍 👎 👏 👐 👑 👒 👓 👔 👕 👖 👗 👘 + 👙 👚 👛 👜 👝 👞 👟 👠 👡 👢 👣 👤 👥 👦 👧 👨 + 👩 👪 👮 👯 👺 👻 👼 👽 👾 👿 💀 💁 💂 💃 💄 💅 ); + + +while (<>) { + if (/[a-f0-9:]+:[a-f0-9:]+/) { + ($b, $m, $a) = ($`, $&, $'); + print $b.join(" ", map { $emoji[$_] } map hex, split /:/, $m)." ".$a; + } +} diff --git a/zen/tools/init_IPFS_with_cesium_loginKEY.sh b/zen/tools/init_IPFS_with_cesium_loginKEY.sh new file mode 100755 index 0000000..2588aa7 --- /dev/null +++ b/zen/tools/init_IPFS_with_cesium_loginKEY.sh @@ -0,0 +1,115 @@ +#!/bin/bash +{ +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +[[ -f ~/.zen/secret ]] && echo "Please rm -f ~/.zen/secret* before running $ME" && exit 1 + +echo '>>>>>>> METAVERSE KEY CREATION <<<<<<<< + + __ __ ____ + __/ // /_______ ______ __________ ___ / __ \ + /_ _ __/ ___/ | /| / / __ `/ ___/ __ `__ \/ / / / +/_ _ __(__ )| |/ |/ / /_/ / / / / / / / / /_/ / + /_//_/ /____/ |__/|__/\__,_/_/ /_/ /_/ /_/\____/ + +CESIUM KEY + +CHOOSE YOU LOGIN (min 8 car. best is more than 6 words!!)... +or LEAVE BLANK and HIT ENTER FOR diceware AUTO GENERATION +' +read salt +[[ $salt == "" ]] && echo "Hit ENTER again to continue" && read salt +[[ $salt != "" ]] && echo "CHOOSE PASSWORD?" && read pepper && [[ $pepper == "" ]] && echo "AARRRRGGG why no pepper? exiting" && exit 1 + +if [[ "$salt" == "" && "$pepper" == "" ]]; then + echo ' + ._ _ ._ _ ._ _ _ ._ o _ + | | | | | (/_ | | | (_) | | | (_ + + diceware passphrase generator...' + # INSTALL diceware files ## TODO REPLACE WITH ipfs links + + [[ ! -f ~/.zen/astroport/zen/tools/diceware.sh ]] \ + && mkdir -p ~/.zen/astroport/zen/tools/ \ + && curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/diceware.sh -o ~/.zen/astroport/zen/tools/diceware.sh \ + && chmod +x ~/.zen/astroport/zen/tools/diceware.sh + + [[ ! -f ~/.zen/astroport/zen/tools/diceware-wordlist.txt ]] \ + && curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/diceware-wordlist.txt -o ~/.zen/astroport/zen/tools/diceware-wordlist.txt + + # LOGIN (=SALT) + salt="miz $(~/.zen/astroport/zen/tools/diceware.sh 2 | xargs)" + # PASS (=PEPPER) + pepper="$(~/.zen/astroport/zen/tools/diceware.sh 2 | xargs)" +fi +echo "........." + +# CREATE /tmp/secret.dunikey +[[ ! -f ~/.zen/astroport/zen/tools/key_create_dunikey.py ]] \ +&& mkdir -p ~/.zen/astroport/zen/tools/ \ +&& curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/key_create_dunikey.py -o ~/.zen/astroport/zen/tools/key_create_dunikey.py \ +&& chmod +x ~/.zen/astroport/zen/tools/key_create_dunikey.py + +python3 ~/.zen/astroport/zen/tools/key_create_dunikey.py "$salt" "$pepper" +sleep 1 +[[ ! -f /tmp/secret.dunikey ]] && echo "AARRRRGGG problem happens making your secret.dunikey exiting" && exit 1 +[[ -f /tmp/secret.dunikey ]] && cp -f /tmp/secret.dunikey ~/.zen/ + +echo "ZENID=\"$salt\" +ZENPWD=\"$pepper\" +" + +g1pub=$(cat ~/.zen/secret.dunikey | grep "pub" | cut -d ' ' -f 2) +g1priv=$(cat ~/.zen/secret.dunikey | grep "sec" | cut -d ' ' -f 2) + +# make ScutlleButt secret key +ssbpub=$(echo $g1pub | base58 -d | base64) +ssbpriv=$(echo $g1priv | base58 -d | base64 | tr -d "\n" ) +cat > ~/.zen/secret < ~/.zen/secret.june +echo "$pepper" >> ~/.zen/secret.june + +chmod 400 ~/.zen/secret* +echo "~/.zen/secret(s) are OK !" + +# MODIFY ~/.ipfs/config +[[ ! -f ~/.zen/astroport/zen/tools/crypto_pb2.py ]] \ +&& mkdir -p ~/.zen/astroport/zen/tools/ \ +&& curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/crypto_pb2.py -o ~/.zen/astroport/zen/tools/crypto_pb2.py \ + +[[ ! -f ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py ]] \ +&& mkdir -p ~/.zen/astroport/zen/tools/ \ +&& curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py -o ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py \ +&& chmod +x ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py + +ipfs_ID=$(python3 ~/.zen/astroport/zen/tools/create_ipfsnodeid_from_tmp_secret.dunikey.py) +echo $ipfs_ID > ~/.zen/secret.ipfs +source ~/.zen/secret.ipfs +cat ~/.zen/secret.ipfs +echo +echo +echo "########################################################" +echo "Welcome to the G1 DRIVE" +echo "Configure IPFS: copy the lines above in your console" +echo "then run jq commands and make your IPFS NODE https://Cesium.app compatible" +echo " -> Read your Cesium messages to find how to Bootstrap your node" +echo +echo "cp -f ~/.ipfs/config ~/.ipfs/config.old" +echo "jq -r --arg PeerID \"\$PeerID\" '.Identity.PeerID=\$PeerID' ~/.ipfs/config > /tmp/config.tmp" +echo "jq -r --arg PrivKEY \"\$PrivKEY\" '.Identity.PrivKey=\$PrivKEY' /tmp/config.tmp > ~/.ipfs/config" +echo "" + +[[ -f /tmp/init_IPFS_with_cesium_loginKEY.sh ]] && cp -f /tmp/init_IPFS_with_cesium_loginKEY.sh ~/.zen/astroport/zen/tools/ +rm -f /tmp/secret.dunikey +} diff --git a/zen/tools/ipfs_to_g1.py b/zen/tools/ipfs_to_g1.py new file mode 100755 index 0000000..f34b750 --- /dev/null +++ b/zen/tools/ipfs_to_g1.py @@ -0,0 +1,10 @@ +#!/usr/bin/env python3 + +import sys, base58 + +ID = sys.argv[1] +hexFmt = base58.b58decode(ID) +noTag = hexFmt[6:] +b58Key = base58.b58encode(noTag).decode() + +print(b58Key) diff --git a/zen/tools/key_create_dunikey.py b/zen/tools/key_create_dunikey.py new file mode 100755 index 0000000..de0673b --- /dev/null +++ b/zen/tools/key_create_dunikey.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# This Python script gets Duniter creddentials as arguments, and writes a PubSec file that should be compatible with Cesium and Silkaj(DuniterPy) clients. +# launch with : +# python3 key_create_dnuikey.py + +# depends on duniterpy 0.56 + +### Licence - WTFPL +# This script was written my Matograine, in the hope that it will be helpful. +# Do What The Fuck you like with it. There is : +# * no guarantee that this will work +# * no support of any kind +# +# If this is helpful, please consider making a donation to the developper's pubkey : 78ZwwgpgdH5uLZLbThUQH7LKwPgjMunYfLiCfUCySkM8 +# Have fun + +from sys import argv +from duniterpy.key import SigningKey + +# path to save to +path = "/tmp/secret.dunikey" + +key = SigningKey.from_credentials(argv[1], argv[2], None) +key.save_pubsec_file(path) +print( + key.pubkey, +) diff --git a/zen/tools/make_G1SSB_secret.sh b/zen/tools/make_G1SSB_secret.sh new file mode 100755 index 0000000..e5c9bf8 --- /dev/null +++ b/zen/tools/make_G1SSB_secret.sh @@ -0,0 +1,400 @@ +#!/bin/bash +{ +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +#### ARM / X64 NOT USED THERE +MACHINE_TYPE=`uname -m` +[ ${MACHINE_TYPE:0:3} == 'arm' ] && isARM="YES" + +if [[ "$1" == "RAZ" ]]; then + echo "~/.SSB_ORIGIN is made for $(whoami)" + [[ -d ~/.ssb_$(whoami) ]] && mv ~/.ssb_$(whoami) ~/.SSB_ORIGIN + rm -Rf ~/.ssb* +else +echo ' +######################################################################## +# \\/// +# qo-op +############# '$MY_PATH/$ME' +######################################################################## +# Make Astroport Station Account +# +# You should already be runing ipfs daemon + +######################################################################## +# - Install silkaj youtube-dl & sbotc +# - Backup any ~/.ssb to ~/.ssb_$USER +# - Creates ~/.ssb_astroport and link it to ~/.ssb +# - Ask for credentials (or auto) +# - CREATE "~/.ssb/secret" & "~/.ssb/secret.dunikey" of you +# !!! KEEP CREDENTIALS IN ~/.zen/secret.astroport.key +######################################################################## +# UnInstall and Recover your ~/.ssb_$USER +# cd && rm -Rf ~/.zen && rm ~/.ssb && mv ~/.ssb_$USER ~/.ssb +######################################################################## +I encourage you to read any code you download. +This one is not perfect, but will not harm your system... + +Install IPFS (compatible with ARM and X64) +curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/.install/ipfs_alone.sh | bash + +HIT ENTER TO CONTINUE +' +read letsgo +fi +# "ipfs daemon" MUST be RUNNING +YOU=$(ps auxf --sort=+utime | grep -w ipfs | grep -v -E 'color=auto|grep' | tail -n 1 | cut -d " " -f 1) +[[ "$YOU" == "" ]] && echo "EXIT! PLEASE INSTALL & RUN ipfs daemon WITH curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/.install/ipfs_alone.sh | bash " && exit 1 + +# ~/.zen is ASTROPORT living place. +[[ ! -d ~/.zen/astroport ]] && mkdir -p ~/.zen/astroport + +# IS git THERE ? +[[ ! $(which git) ]] && sudo apt install git -y +[[ ! $(which figlet) ]] && sudo apt install figlet -y +[[ ! $(which lolcat) ]] && sudo apt install lolcat -y + +# USE git pull OR git clone +if [[ -f ~/.zen/astroport/install.sh ]]; then + cd ~/.zen/astroport && git pull +else + cd ~/.zen + git clone https://git.p2p.legal/axiom-team/astroport.git +fi + +cd ~/.zen/astroport +# LETS GO +# Install nvm +echo ' + __ _ + ____ ____ ____/ /__ (_)____ + / __ \/ __ \/ __ / _ \ / / ___/ + / / / / /_/ / /_/ / __/ / (__ ) +/_/ /_/\____/\__,_/\___/ __/ /____/ + /___/ + +' | lolcat +if [[ ! $(which node) ]]; then + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash + source ~/.bashrc + export NVM_DIR="$HOME/.nvm" + [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm + [ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion + source ~/.bashrc + nvm install 12 + nvm use 12 +fi + +echo ' + ____ ___ _____ _________ + / __ \/ | / ___// _/ ___/ + / / / / /| | \__ \ / / \__ \ +/ /_/ / ___ |___/ // / ___/ / +\____/_/ |_/____/___//____/ + +' | lolcat + +nodename=$(curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/nodename | bash) + +if [[ ! $(which oasis) ]]; then + echo "INSTALL.... http://$nodename" +# echo "ENTER Station accessible Network name !!! Suggestion : $nodename" +# read nodename + # Install nvm + if [[ ! $(which node) || ! $(which npm) ]]; then + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash + source ~/.bashrc + export NVM_DIR="$HOME/.nvm" + nvm install --lts + fi + npm -g install sodium-native ssb-backlinks ssb-ws ssb-links ssb-query ssb-secret-blob ssb-private + npm -g install fraction/oasis#semver: + npm -g install ssb-server ## ADD SSB-SERVER FOR PRIVATE SSB MESSAGING (TODO: integrate Feedless modules on LOVELand Portal) +else + echo "Stopping OASIS" + ssbD=$(ps auxf --sort=+utime | grep -w oasis | grep -v -E 'color=auto|grep' | awk '{print $2}') + ssbD+=$(ps auxf --sort=+utime | grep -w ssb-server | grep -v -E 'color=auto|grep' | awk '{print $2}') + + for tokill in $ssbD; do + kill -9 $tokill + done + + +fi + + + +# INSTALL Silkaj, CLI for Duniter +echo '************************************************************** + __ ___ +(_ | | |/ /\ | +__) _|_ |_ |\ /--\ \_| + +#Duniter communication client... +' | lolcat + +sudo apt update || true +sudo apt install ssmtp mpack libffi-dev build-essential qrencode jq bc gawk -y + +export PATH=$PATH:~/.local/bin +if [[ ! $(which silkaj) ]]; then + libzzz=$(sudo apt-cache search libsodium | awk '{print $1}' | grep libsodium2) + [[ $libzzz == "" ]] && libzzz=$(sudo apt-cache search libsodium | awk '{print $1}' | grep libsodium1) + sudo apt install $libzzz -y + sudo apt install python3-pip python3-setuptools python3-wheel -y + pip3 install base58 + pip3 install silkaj --user + echo 'PATH=$PATH:$HOME/.local/bin' >> ~/.bashrc && source ~/.bashrc +fi + +# INSTALL sbotc +echo ' + __ __ + _____/ /_ ____ / /______ + / ___/ __ \/ __ \/ __/ ___/ + (__ ) /_/ / /_/ / /_/ /__ +/____/_.___/\____/\__/\___/ + +ScuttleButt communication client... +' | lolcat +if [[ ! $(which sbotc) ]]; then + sudo apt install libsodium-dev build-essential imagemagick -y + cd /tmp/ + git clone https://git.scuttlebot.io/%25133ulDgs%2FoC1DXjoK04vDFy6DgVBB%2FZok15YJmuhD5Q%3D.sha256 sbotc + cd sbotc + make + sudo make install + cd ~/.zen/astroport +fi + + +echo ' + __ __ + ____ ______/ /__________ ____ ____ _____/ /_ + / __ `/ ___/ __/ ___/ __ \/ __ \/ __ \/ ___/ __/ +/ /_/ (__ ) /_/ / / /_/ / /_/ / /_/ / / / /_ +\__,_/____/\__/_/ \____/ .___/\____/_/ \__/ + /_/ + +ONBOARDING activation... linking ~/.ssb to ~/.ssb_astroport + +' | lolcat +# CREATE ~/.ssb_astroport +[[ ! -d ~/.ssb_astroport ]] && mkdir -p ~/.ssb_astroport + +# If exists backup ~/.ssb to ~/.ssb_$USER SSB (one time only !) +[[ -d ~/.ssb_$USER ]] && echo "BACKUP already existing... Restore it : rm -Rf ~/.ssb && mv ~/.ssb_$USER ~/.ssb # and Try again..." && exit 1 + +if [[ -d ~/.ssb ]]; then + [[ -f ~/.ssb/manifest.json ]] && cp -f ~/.ssb/manifest.json ~/.ssb_astroport/ + [[ -f ~/.ssb/conn.json ]] && cp -f ~/.ssb/conn.json ~/.ssb_astroport/ + [[ -f ~/.ssb/gossip.json ]] && cp -f ~/.ssb/gossip.json ~/.ssb_astroport/ +fi + +# BACKUP ACTUAL SSB ACCOUNT +[[ -d ~/.ssb ]] && mv ~/.ssb ~/.ssb_$USER + +# Symlink ~/.ssb -> ~/.ssb_astroport +[[ -L ~/.ssb ]] && rm ~/.ssb +[[ -d ~/.ssb_astroport ]] && ln -s ~/.ssb_astroport ~/.ssb + +cd ~/.ssb/ + +[[ ! -f ~/.ssb/manifest.json ]] && cp ~/.zen/astroport/.install/templates/ssb/manifest.json ~/.ssb/ && echo "manifest.json OK" + +echo '>>>>>>> METAVERSE KEY CREATION <<<<<<<< + + __ __ ____ + __/ // /_______ ______ __________ ___ / __ \ + /_ _ __/ ___/ | /| / / __ `/ ___/ __ `__ \/ / / / +/_ _ __(__ )| |/ |/ / /_/ / / / / / / / / /_/ / + /_//_/ /____/ |__/|__/\__,_/_/ /_/ /_/ /_/\____/ + +KEY + +CHOOSE YOU LOGIN (min 8 car. best is more than 6 words!!)... +or LEAVE BLANK and HIT ENTER FOR diceware AUTO GENERATION +' | lolcat +read salt +[[ $salt != "" ]] && echo "CHOOSE PASSWORD?" && read pepper && [[ $pepper == "" ]] && exit 1 + +if [[ "$salt" == "" && "$pepper" == "" ]]; then + +echo ' +._ _ ._ _ ._ _ _ ._ o _ +| | | | | (/_ | | | (_) | | | (_ + +diceware passphrase generator...' | lolcat +# INSTALL diceware files ## TODO REPLACE WITH ipfs links + +[[ ! -f ~/.zen/astroport/zen/tools/diceware.sh ]] \ +&& mkdir -p ~/.zen/astroport/zen/tools/ \ +&& curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/diceware.sh -o ~/.zen/astroport/zen/tools/diceware.sh \ +&& chmod +x ~/.zen/astroport/zen/tools/diceware.sh + +[[ ! -f ~/.zen/astroport/zen/tools/diceware-wordlist.txt ]] \ +&& curl -s https://git.p2p.legal/axiom-team/astroport/raw/master/zen/tools/diceware-wordlist.txt -o ~/.zen/astroport/zen/tools/diceware-wordlist.txt + +# LOGIN (=SALT) +salt="$(~/.zen/astroport/zen/tools/diceware.sh 6 | xargs)" +# PASS (=PEPPER) +pepper="$(~/.zen/astroport/zen/tools/diceware.sh 4 | xargs)" + +fi +echo "........." + +rm -f ~/.zen/secret.astroport.key +echo "#20200606 ASTROPORT METAVERSE #SWARM0 IDENTITY +ZENID=\"$salt\" +ZENPWD=\"$pepper\"" > ~/.zen/secret.astroport.key +sleep 1 + +# CREATE ~/.ssb/secret.dunikey +python3 ~/.zen/astroport/zen/tools/key_create_dunikey.py "$salt" "$pepper" +sleep 1 +[[ ! -f /tmp/secret.dunikey ]] && echo "AARRRRGGG problem happens making your secret.dunikey" && exit 1 +[[ -f /tmp/secret.dunikey ]] && rm -f ~/.ssb/secret.dunikey && mv /tmp/secret.dunikey ~/.ssb/secret.dunikey + +# CREATE SSB secret +g1pub=$(cat ~/.ssb/secret.dunikey | grep "pub" | cut -d ' ' -f 2) +echo "G1PUB=\"$g1pub\"" >> ~/.zen/secret.astroport.key + +g1priv=$(cat ~/.ssb/secret.dunikey | grep "sec" | cut -d ' ' -f 2) +ssbpub=$(echo $g1pub | base58 -d | base64) +ssbpriv=$(echo $g1priv | base58 -d | base64 | tr -d "\n" ) + +rm -f ~/.ssb/secret +cat > ~/.ssb/secret <> ~/.zen/secret.astroport.key + +echo " + _ +|__|_ \ / _ o | _. +|_ |_ \/ (_) | | (_| + +Your Identity is created !! +REMEMBER TO KEEP your secret files SECRET !!! + +Your public name : @${ssbpub}.ed25519 +Your G1 WALLET : $g1pub + +" + +chmod 400 ~/.ssb/secret +chmod 400 ~/.ssb/secret.dunikey + +echo ' + __ + __________/ /_ + / ___/ ___/ __ \ + (__ |__ ) /_/ / +/____/____/_.___/ + +NEW IDENTITY ACTIVATED in ~/.ssb/secret + +' | lolcat + +echo ' + _________ + / ____< / + / / __ / / +/ /_/ // / +\____//_/ + +IDENTITY CREATED in ~/.ssb/secret.dunikey + +Install https://cesium.app to use it !! + +' | lolcat + +cat ~/.zen/secret.astroport.key + +echo ' + +Now you are going to join #Swarm0 IPFS Metaverse +ACTIVATE METAVERSE #SWARM0 INIT SEQUENCE... +ALPHA - ALPHA - ALPHA - ALPHA + +' +echo "Starting SSB SERVER... wait 10 seconds..." +ssb-server start & + +sleep 10 + +if [[ $isARM ]]; then + echo "Starting OASIS..." + oasis --allow-host $nodename --host $nodename & + sleep 7 +else +# Intall Patchwork + if [[ ! $(which ssb-patchwork) ]]; then + wget https://github.com/ssbc/patchwork/releases/download/v3.18.0/ssb-patchwork_3.18.0_amd64.deb -O /tmp/patchwork.deb + sudo dpkg -i /tmp/patchwork.deb + sleep 1 + rm /tmp/patchwork.deb + + ## npm install way + ##npm install --global ssb-patchwork + sleep 2 + + # Start Patchwork + #[[ $(which ssb-patchwork) ]] && ssb-patchwork || echo -e "${c_red}Patchwork is not installed$c_" + fi +fi + +~/.zen/astroport/zen/ssb_INIT.sh +~/.zen/astroport/zen/cron_VRFY.sh + +echo -e "Finished... + +######################################################################## +${c_light}IF SOMETHING WENT WRONG REPORT AN ISSUE +https://git.p2p.legal/axiom-team/astroport/issues$c_ +######################################################################## +MAKE SOME TEST + +1. Test IPFS Layer +ipfs id + +2. Test SSB Layer +sbotc whoami + +3. Test G1 Layer +silkaj balance $g1pub + +4. Test #Swarm0 Activation +crontab -l + +5. Test OASIS +WARNING: If tour installation is working on Pathwork, Oasis is disabled +http://$nodename:3000 + +IF EVERYTHING IS OK +ADD oasis TO YOUR system AUTOSTART !!! +oasis --allow-host $nodename --host $nodename + +THANK YOU. Now it is time to connect to your friends... + +6. ADD ScuttleButt PUB Invitation (With Oasis: http://$nodename:3000/settings) +${c_light}oasis.astroport.com:8008:@UeiA9iqZ0/XTjmYBht230KGr44bsr+Tl5BXSUDFv8vo=.ed25519~xfUSq/J2zLeFwrvvHie4iXI/GAzybUu7Zs9T7/PgZ+w= $c_ + +" +exit 0 +} diff --git a/zen/tools/natools.py b/zen/tools/natools.py new file mode 100755 index 0000000..9de1187 --- /dev/null +++ b/zen/tools/natools.py @@ -0,0 +1,182 @@ +#!/usr/bin/env python3 + +""" + CopyLeft 2020 Pascal Engélibert + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +""" + +__version__ = "1.0" + +import os, sys, duniterpy.key, libnacl.sign, base58, base64, getpass + +def getargv(arg:str, default:str="", n:int=1, args:list=sys.argv) -> str: + if arg in args and len(args) > args.index(arg)+n: + return args[args.index(arg)+n] + else: + return default + +def read_data(data_path, b=True): + if data_path == "-": + if b: + return sys.stdin.read().encode() + else: + return sys.stdin.read() + else: + return open(os.path.expanduser(data_path), "rb" if b else "r").read() + +def write_data(data, result_path): + if result_path == "-": + os.fdopen(sys.stdout.fileno(), 'wb').write(data) + else: + open(os.path.expanduser(result_path), "wb").write(data) + +def encrypt(data, pubkey): + return duniterpy.key.PublicKey(pubkey).encrypt_seal(data) + +def decrypt(data, privkey): + return privkey.decrypt_seal(data) + +def sign(data, privkey): + return privkey.sign(data) + +def verify(data, pubkey): + try: + sys.stderr.write("Signature OK!\n") + return libnacl.sign.Verifier(duniterpy.key.PublicKey(pubkey).hex_pk()).verify(data) + except ValueError: + sys.stderr.write("Bad signature!\n") + exit(1) + +def get_privkey(privkey_path, privkey_format): + if privkey_format == "pubsec": + if privkey_path == "*": + privkey_path = "privkey.pubsec" + return duniterpy.key.SigningKey.from_pubsec_file(privkey_path) + + elif privkey_format == "cred": + if privkey_path == "*": + privkey_path = "-" + if privkey_path == "-": + return duniterpy.key.SigningKey.from_credentials(getpass.getpass("Password: "), getpass.getpass("Salt: ")) + else: + return duniterpy.key.SigningKey.from_credentials_file(privkey_path) + + elif privkey_format == "seedh": + if privkey_path == "*": + privkey_path = "authfile.seedhex" + return duniterpy.key.SigningKey.from_seedhex(read_data(privkey_path, False)) + + elif privkey_format == "wif": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_file(privkey_path) + + elif privkey_format == "wifh": + if privkey_path == "*": + privkey_path = "authfile.wif" + return duniterpy.key.SigningKey.from_wif_or_ewif_hex(privkey_path) + + elif privkey_format == "ssb": + if privkey_path == "*": + privkey_path = "secret" + return duniterpy.key.SigningKey.from_ssb_file(privkey_path) + + elif privkey_format == "key": + if privkey_path == "*": + privkey_path = "authfile.key" + return duniterpy.key.SigningKey.from_private_key(privkey_path) + +fmt = { + "raw": lambda data: data, + "16": lambda data: data.hex().encode(), + "32": lambda data: base64.b32encode(data), + "58": lambda data: base58.b58encode(data), + "64": lambda data: base64.b64encode(data), + "64u": lambda data: base64.urlsafe_b64encode(data), + "85": lambda data: base64.b85encode(data), +} + +def show_help(): + print("""Usage: +python3 natools.py [options] + +Commands: + encrypt Encrypt data + decrypt Decrypt data + sign Sign data + verify Verify data + +Options: + -f Private key format (default: cred) + key cred pubsec seedh ssb wif wifh + -i Input file path (default: -) + -k Privkey file path (* for auto) (default: *) + -p Pubkey (base58) + -o Output file path (default: -) + --noinc Do not include msg after signature + -O Output format: raw 16 32 58 64 64u 85 (default: raw) + + --help Show help + --version Show version + --debug Debug mode (display full errors) + +Note: "-" means stdin or stdout. +""") + +if __name__ == "__main__": + + if "--help" in sys.argv: + show_help() + exit() + + if "--version" in sys.argv: + print(__version__) + exit() + + privkey_format = getargv("-f", "auto") + data_path = getargv("-i", "-") + privkey_path = getargv("-k", "*") + pubkey = getargv("-p") + result_path = getargv("-o", "-") + output_format = getargv("-O", "raw") + + try: + if sys.argv[1] == "encrypt": + write_data(fmt[output_format](encrypt(read_data(data_path), pubkey)), result_path) + + elif sys.argv[1] == "decrypt": + write_data(fmt[output_format](decrypt(read_data(data_path), get_privkey(privkey_path, privkey_format))), result_path) + + elif sys.argv[1] == "sign": + data = read_data(data_path) + signed = sign(data, get_privkey(privkey_path, privkey_format)) + + if "--noinc" in sys.argv: + signed = signed[:len(signed)-len(data)] + + write_data(fmt[output_format](signed), result_path) + + elif sys.argv[1] == "verify": + write_data(fmt[output_format](verify(read_data(data_path), pubkey)), result_path) + + else: + show_help() + + except Exception as e: + if "--debug" in sys.argv: + 0/0 # DEBUG MODE + sys.stderr.write("Error: {}\n".format(e)) + show_help() + exit(1) diff --git a/zen/tools/nodename b/zen/tools/nodename new file mode 100755 index 0000000..349fd0b --- /dev/null +++ b/zen/tools/nodename @@ -0,0 +1,59 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) + @benoit +# Version: 0.4 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +{ # Full reading before execution ! +# echo ' +# +# __ ___ _ ___ +# / |/ /_ __ ____ ____ _____ ___ ___ (_)___/__ \ +# / /|_/ / / / / / __ \/ __ `/ __ `__ \/ _ \ / / ___// _/ +# / / / / /_/ / / / / / /_/ / / / / / / __/ / (__ )/_/ +# /_/ /_/\__, / /_/ /_/\__,_/_/ /_/ /_/\___/ /_/____/(_) +# /____/ +# +# ' | lolcat +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" + +# check if -w is specified: +# Will look for the public IP of my gateway +# if my node is a Demilitarized Zone (DMZ) +WAN=$1 + +# GNU DEBIAN Core install extension +[[ ! $(which nslookup) ]] && sudo apt-get update && sudo apt-get install dnsutils -y + +# What is myIP? +export myIP=$(hostname -I | awk '{print $1}' | head -n 1) +# Check if IP is from reserved LAN addresses +export isLAN=$(echo $myIP | grep -E "/(^127\.)|(^192\.168\.)|(^10\.)|(^172\.1[6-9]\.)|(^172\.2[0-9]\.)|(^172\.3[0-1]\.)|(^::1$)|(^[fF][cCdD])/") +# Find default route gateway +export myRouter=$(sudo route -n | grep ' UG ' | tail -n 1 | awk '{print $2}') + +# Ask to the router my name (BOX DNS or system defined) +[[ $isLAN ]] && export NODENAME=$(sudo nslookup $myIP $myRouter -timeout=2 | grep 'name =' | awk -F ' = ' '{print $2}' | sed 's/\.$//') \ +|| export NODENAME=$(sudo nslookup $myIP -timeout=2 | grep 'name =' | awk -F ' = ' '{print $2}' | sed 's/\.$//') + +if [[ -z "$NODENAME" && "$WAN" == "-w" ]]; then + IP=`wget --quiet http://brouits.free.fr/ip.php -O- | sed 's/\r//'` + NODENAME=`nslookup $IP -timeout=2 | head -1 | cut -d'=' -f 2 | sed -E 's/^ *(.*)\.$/\1/'` +fi + +# We must have that nodename, perhaps BOX DNS doesn't exist... +[[ $NODENAME == "" ]] && [[ $isLAN ]] && export NODENAME=$(sudo nslookup $myIP -timeout=2 | grep 'name =' | awk -F ' = ' '{print $2}' | sed 's/\.$//') +# OK no DNS is answering, let's be /etc/hostname +[[ $NODENAME == "" ]] && NODENAME=$(cat /etc/hostname) + +NODENAME=$(echo "$NODENAME" | awk '{print tolower($1)}' | tail -n1) + +[[ ! $(ping $NODENAME -c 1 2>/dev/null) ]] && NODENAME=$(echo $NODENAME | awk -F '.' '{ print $1 }') && NODENAME=$(echo "$NODENAME.local") +[[ ! $(ping $NODENAME -c 1 2>/dev/null) ]] && NODENAME="localhost" + +# I can tell my name is (in lowercase) +echo "$NODENAME" + +} # Full reading before execution ! diff --git a/zen/tools/sbotc_check_invite.sh b/zen/tools/sbotc_check_invite.sh new file mode 100755 index 0000000..9cf33b6 --- /dev/null +++ b/zen/tools/sbotc_check_invite.sh @@ -0,0 +1,26 @@ +#!/bin/bash +# Author @f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 + +invite="${1?invite}" +code="${invite##*~}" +addr="${invite%%~*}" +feed="${addr##*:}" +hostname="${addr%:*}" +host="${hostname%:*}" +port="${hostname##*:}" + +out="$(sbotc -s "$host" -p "$port" -k "$feed" -K "$code" -t async invite.use {} 2>&1)" +if echo "$out" | grep -q 'feed to follow is missing' +then + echo success +elif echo "$out" | grep -q 'method:invite,use is not in list of allowed methods' +then + echo invalid/expired + exit 1 +else + echo fail + echo "$out" + echo trying another method: + sbotc -s "$host" -p "$port" -k "$feed" -K "$code" -t source blobs.get '' + exit 1 +fi diff --git a/zen/tools/sbotc_send_private.sh b/zen/tools/sbotc_send_private.sh new file mode 100755 index 0000000..9768108 --- /dev/null +++ b/zen/tools/sbotc_send_private.sh @@ -0,0 +1,14 @@ +#!/bin/bash +#recps='@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 @5XaVcAJ5DklwuuIkjGz4lwm2rOnMHHovhNg7BFFnyJ8=.ed25519' +recps=$1 +file=$2 + +name=${file##*/} +link="$(sblob encrypt "$file")" +type="$(file -b --mime-type "$file")" +id=${link%?unbox=*} +key=${link#*?unbox=} +size="$(sbotc -e blobs.size "$id")" +recps_array="[$(for recp in $recps; do printf '"%s"\n' "$recp"; done | paste -sd,)]" +sbotc private.publish '{"type":"post","text":"['"$name"']('"$link"')","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'","query":{"unbox":"'"$key"'"}}],"recps":'"$recps_array"'}' "$recps_array" + diff --git a/zen/tools/search b/zen/tools/search new file mode 100755 index 0000000..f5b8195 --- /dev/null +++ b/zen/tools/search @@ -0,0 +1,16 @@ +#!/bin/bash +clear +echo "------------------------------------------------------------------------------" +if [ "$1" == "" ]; then + echo " Nothing to search for!" +else + echo " Searching for "$1" recursively. Please Wait..." + echo "------------------------------------------------------------------------------" + grep -h -r --exclude=B00 -H --colour=always "$1" ./ +fi +echo "------------------------------------------------------------------------------" +if [ "$2" != "" ]; then + echo " To replace \"$1\" whith \"$2\", please run" + echo " grep -rl '$1' ./ | xargs sed -i 's/$1/$2/g'" +fi + diff --git a/zen/tools/secret2dunikey.sh b/zen/tools/secret2dunikey.sh new file mode 100755 index 0000000..2fb7a49 --- /dev/null +++ b/zen/tools/secret2dunikey.sh @@ -0,0 +1,27 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 1.0 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# This script creates ~/.ssb/secret.dunikey from SSB secret +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +######################################################################## +# \\/// +# qo-op +############# +[[ ! -f ~/.ssb/secret ]] && echo "ERROR Your SSB secret is missing... EXIT" && exit 1 +rm -f ~/.ssb/secret.dunikey +ssbpub=$(cat ~/.ssb/secret | grep public\" | cut -d ' ' -f 4 | cut -d '.' -f 1 | sed s/\"//g) +ssbpriv=$(cat ~/.ssb/secret | grep private\" | cut -d ' ' -f 4 | cut -d '.' -f 1 | sed s/\"//g) +g1pub=$(echo $ssbpub | base64 -d | base58) +g1priv=$(echo $ssbpriv | base64 -d | base58) +cat > ~/.ssb/secret.dunikey < + +scriptName="${0##*/}" + +declare -i DEFAULT_TIMEOUT=20 +declare -i DEFAULT_INTERVAL=1 +declare -i DEFAULT_DELAY=1 + +# Timeout. +declare -i timeout=DEFAULT_TIMEOUT +# Interval between checks if the process is still alive. +declare -i interval=DEFAULT_INTERVAL +# Delay between posting the SIGTERM signal and destroying the process by SIGKILL. +declare -i delay=DEFAULT_DELAY + +function printUsage() { + cat < 0)); do + sleep $interval + kill -0 $$ || exit 0 + ((t -= interval)) + done + + # Be nice, post SIGTERM first. + # The 'exit 0' below will be executed if any preceeding command fails. + kill -s SIGTERM $$ && kill -0 $$ || exit 0 + sleep $delay + kill -s SIGKILL $$ +) 2> /dev/null & + +exec "$@" diff --git a/zen/tools/wav2mp3.sh b/zen/tools/wav2mp3.sh new file mode 100755 index 0000000..60b0784 --- /dev/null +++ b/zen/tools/wav2mp3.sh @@ -0,0 +1,2 @@ +#!/bin/bash +find . -type f -name '*.wav' -exec bash -c 'ffmpeg -i "$0" -c:a libmp3lame -q:a 2 "${0/%wav/mp3}"' '{}' \; diff --git a/zen/xbian_vstream.sh b/zen/xbian_vstream.sh new file mode 100755 index 0000000..fd9991d --- /dev/null +++ b/zen/xbian_vstream.sh @@ -0,0 +1,68 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.12.05 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +# Transfert ~/astroport/files to IPFS +# Create Astroport(pastebin) index for Xbian/Vstream +# ipfs publish with xbian key +######################################################################## +[[ ! -f ~/.zen/ipfs/.${IPFSNODEID}/_xbian.zuid ]] && echo "ERROR no _xbian.zuid" && exit 1 + +mkdir -p ~/astroport/films +mkdir -p ~/astroport/series +mkdir -p ~/astroport/animes + +# GET XZUID +XZUID=$(cat ~/.zen/ipfs/.${IPFSNODEID}/_xbian.zuid) + +echo "CREATE ~/.zen/ipfs/xbian/F$XZUID FROM ~/astroport/films" +echo "-----------------------------------------------------------------" +# CREATE Vstream/Pastebin file format +# https://github.com/Kodi-vStream/venom-xbmc-addons/wiki/Voir-et-partager-sa-biblioth%C3%A8que-priv%C3%A9e#d%C3%A9clarer-des-films + +echo "ADDING ~/astroport/films/ to IPFS" +echo "-----------------------------------------------------------------" +AFSHARE=$(ipfs add -rq ~/astroport/films/ | tail -n 1) +[[ $AFSHARE == "" ]] && echo "ipfs add ERROR" && exit 1 + +echo "WRITE ~/.zen/ipfs/xbian/F$XZUID Films index" +echo "-----------------------------------------------------------------" +mkdir -p ~/.zen/ipfs/xbian +echo "TITLE;URLS" > ~/.zen/ipfs/xbian/F${XZUID} +for file in ~/astroport/films/* +do + filename=$(basename -- "$file") + extension="${filename##*.}" + filena="${filename%.*}" + echo "$filena;http://localhost:8080/ipfs/$AFSHARE/$filename" >> ~/.zen/ipfs/xbian/F${XZUID} +done + +### TODO +### ADD Animes +### ADD Series + +echo "PUBLISH ~/.zen/ipfs to IPNS self" +echo "-----------------------------------------------------------------" +MIPFS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --quieter /ipfs/$MIPFS) + +ipfs cat /ipns/$NODEIPNS/xbian/F${XZUID} + +echo "SYNCing SWARM..." +echo "-----------------------------------------------------------------" +~/.zen/astroport/zen/ipfs_SWARM_refresh.sh + +echo "PREPARE my local FASTRXBIAN (films) list from ipfs_swarm" +echo "-----------------------------------------------------------------" +mkdir -p /tmp/xbian/ +echo 'TITLE;URL' > /tmp/xbian/FASTRXBIAN +cat ~/.zen/ipfs_swarm/xbian/F*-* | grep -v -E 'TITLE;URL' >> /tmp/xbian/FASTRXBIAN +cat /tmp/xbian/FASTRXBIAN | uniq > /tmp/xbian/FASTRXBIAN.uniq +mv /tmp/xbian/FASTRXBIAN.uniq /tmp/xbian/FASTRXBIAN + +echo "PUBLISH IPNS with 'xbian' key" +echo "-----------------------------------------------------------------" +ISTREAM=$(ipfs add -qrw /tmp/xbian/ | tail -n 1) +ipfs name publish -k xbian $ISTREAM diff --git a/zen/zen_MAKE.sh b/zen/zen_MAKE.sh new file mode 100755 index 0000000..57e2edd --- /dev/null +++ b/zen/zen_MAKE.sh @@ -0,0 +1,475 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.18 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$ME' creates ZenTag (+ Passenger)' + +ZEN="$1" # Zen amount +ZENSOURCE="$2" # G1 TX HASH OR PASSENGER timestamp ~/.zen/miam/timestamp/ +PASSENGER="$3" # ~/.zen/miam/timestamp/src_id.ext in ZenTag (add to IPFS datastructure) +METADATA="$4" +READ="$5" # Zen amount asked to allow passenger reading +PARK="$6" # Zen amount payed for IPFS PIN by passenger everyday +# Add any "parameters" needed by CONTRACT.sh + +echo " +######################################################################## +# $MY_PATH/$ME $ZEN $ZENSOURCE $PASSENGER $READ $PARK $METADATA +# IPFS DATASTRUCTURE ~/.zen/tag/sha256sum(_tag.uid) +######################################################################## +# [ASTROPORT](https://astroport.com) +########################################################################" + +# If PASSENGER file, then must provide METADATA! +[[ $PASSENGER != "" ]] && [[ $METADATA == "" ]] && echo "ERROR PASSENGER needs METADATA !!! Please verify and correct..." && exit 1 +[[ $READ == "" ]] && READ=1 +[[ $PARK == "" ]] && PARK=10 +############################################## +# NODE ENVIRONEMENT DETECTION +############################################## +IPFSNODEID=$(ipfs id -f='\n') +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +G1PUB=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) +echo "G1SSB Wallet: $G1PUB +IPFS: $IPFSNODEID + __ __ _ _ _______ + _______ _ __ | \/ | / \ | |/ / ____| +|_ / _ \ '_ \ | |\/| | / _ \ | ' /| _| + / / __/ | | | | | | |/ ___ \| . \| |___ +/___\___|_| |_| |_| |_/_/ \_\_|\_\_____| + +" +######################################################################## +# ZenTag is an IPFS/IPNS datastructure shared into IPFS, publish +# ~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~ Draft ~~~~ +# _chain # ZenTag IPFS Hash +# _chain.n # Sequence Number +# _chain.nanodate # Current nanodate +# _chain.prev # Previous IPFS Hash +# _g1.node.creator # G1SSB wallet pubkey +# _tag.issuer # G1TX ISSUER pubkey +# _tag.zensource # G1TX HASH or SSB timestamp +# _ipfs.node.creatorcat # NODE IPFS ID +# _ipfs.publishkey.BB.aes # BB SYMcypher of IPNS publish key +# _ipfs.publishkey.crypt # G1SSB ASYMcypher of IPNS publish key +# _tag.BB.sha # BB sha256sum +# _tag.uid # ZenTag UID +# _tag.zen # ZentTag balance +# _ipns # /ipns/ address +# PASSENGER FILE (put to IPFS and cypher link in IPNS ZenTag) +# _passenger.filename # Passenger filename +# _passenger.ipfs.crypt # G1SSB cyphered IPFS Passenger link +# _passenger.read # Zen value asked for READING +# _passenger.park # Zen value paid for PARKING +# Only if contract executed => add new cyphered links +# _passenger.contract.sh # Zen CONTRACT TODO +# COmpare with code to verify and extend Draft +# TODO: Could be converted into yaml or json if you like... +######################################################################## +#TAG IS MANAGED BY NODE KEY OR BB KEY to change IPNS latest status +#INDEX UPDATE ON CURRENT NODE IPFS SWARM CHANNEL + +[[ $ZEN == "" || $ZENSOURCE == "" ]] && echo "ERROR... Please provide Zen value" && exit 1 + +if [[ "$METADATA" == "" ]]; then + +echo " +# LOVE BANK - PrePaid Card ZenTag +# FULLY BACKED ON IRL G1 LIBRE MONEY (https://cesium.app) +# ____ ____ _____ _____ _ _ +# / ___| _ \| ____| ____| \ | | _______ _ __ +# | | _| |_) | _| | _| | \| | |_ / _ \ '_ \ +# | |_| | _ <| |___| |___| |\ | / / __/ | | | +# \____|_| \_\_____|_____|_| \_| /___\___|_| |_| +# +" +# COMMENT WILL BREAK (G1 <=> ZEN) STRICT RELATION +[[ ! -f ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE ]] && echo "ERROR# UNKNOWN ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE !!" && exit 1 +ISSUER=$(cat ~/.zen/cache/g1_TX_inputs/zen.$ZENSOURCE) +[[ $ISSUER == "" ]] && echo "ERROR# NO ISSUER FOUND FOR TX $ZENSOURCE !!" && exit 1 +echo "ISSUER = $ISSUER SENT TX $ZENSOURCE !!" + +else + +echo " + +_|_ o ._ _ _ _ _|_ _. ._ _ ._ + |_ | | | | (/_ _> |_ (_| | | | |_) + | +SSB PASSENGER ~/.zen/miam/$ZENSOURCE +" + +[[ ! -d ~/.zen/miam/$ZENSOURCE ]] && echo "ERROR# UNKNOWN ~/.zen/miam/$ZENSOURCE !!" && exit 1 +ISSUER=$(cat ~/.zen/miam/$ZENSOURCE/msg_key) +[[ $ISSUER == "" ]] && echo "ERROR# NO ISSUER FOUND FOR miam $ZENSOURCE !!" && exit 1 +echo "ISSUER = SSB Message $ISSUER !!" + + +fi + +######################################################################## +# CREATE ZEN TAG +######################################################################## +# Produce unique 6 words diceware to give a name _tag.uid +AA=$(echo $($MY_PATH/tools/diceware.sh 6 | xargs) | sed s/\ /_/g ) # ZenTag Name = Diceware_6_words +AAH=$(echo -n ${AA} | sha256sum | cut -d ' ' -f 1) # ZenTag Name SHA256 + +# TODO ACTIVATE SWARM UNICITY CHECK +# Create Unique Zentag for all IPFS SWARM! +while [[ $(grep -Rwl "$AA" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.uid) ]]; do + AA=$(echo $($MY_PATH/tools/diceware.sh 6 | xargs) | sed s/\ /_/g ) + AAH=$(echo -n ${AA} | sha256sum | cut -d ' ' -f 1) +done + +# BB key is a 4 word diceware printed on QRCode for Human use. +# SHA256 PASSWORD CHECK FOR ALLOWING WRITE ACTION +BB=$($MY_PATH/tools/diceware.sh 4 | xargs); +BBH=$(echo -n "$BB" | sha256sum | cut -d ' ' -f 1) + +######################################################################## +# INITIAL DATA STRUCTURE +######################################################################## +mkdir -p ~/.zen/tag/${AAH} +# LOG +echo " + _____ _____ _ ____ +|__ /___ _ _|_ _|/ \ / ___| __/\__ + / // _ \ °_ \| | / _ \| | _ \ / + / /| __/ | | | |/ ___ \ |_| | /_ _\ +/____\___|_| |_|_/_/ \_\____| \/ + +******************************************************* +| $AA | $ZEN Zen +******************************************************* +CREATING ~/.zen/tag/${AAH}/ +" + +# COPY ZENTAG TO LOCAL ~/.zen/tag +echo "$AA" > ~/.zen/tag/${AAH}/_tag.uid # Nom du ZenTAG +echo "$ZEN" > ~/.zen/tag/${AAH}/_tag.zen # Tag ZEN amount +echo "${IPFSNODEID}" > ~/.zen/tag/${AAH}/_ipfs.node.creator # NODE IPFS ID +echo "$ZENSOURCE" > ~/.zen/tag/${AAH}/_tag.zensource # ZENSOURCE +echo "$G1PUB" > ~/.zen/tag/${AAH}/_g1.node.creator # CREATOR IPFS NODE G1PUBKEY +echo "$ISSUER" > ~/.zen/tag/${AAH}/_tag.issuer # TX ISSUER G1PUBKEY OR SSB_MESSAGE ID + +######################################################################## +# Create IPNS publishing key ${AA}.key +# ===================================== +# BB encrypted version for IRL use +# G1PUB for current G1SSB Node later access... +# Using sha256sum / openssl / natools.py (libsodium) +######################################################################## +#timestamp=$(date -u +%s%N | cut -b1-13) +keyname="${ZENSOURCE}_${AA}.key" +[[ ! $(ipfs key list | grep -F ${AA}.key) ]] && J0=$(ipfs key gen -t rsa -s 2048 $keyname) +# WRITE BBH for QRCode BB verification +echo "$BBH" > ~/.zen/tag/${AAH}/_tag.BB.sha +# BB pgp symetric publishkey +keyfilename=$(ls ~/.ipfs/keystore/ | tail -n 1) +openssl aes-256-cbc -pbkdf2 -k "$BB" -salt -in ~/.ipfs/keystore/${keyfilename} -out ~/.zen/tag/${AAH}/_ipfs.publishkey.BB.aes +# GIVE IPFS CREATOR NODE ACCESS TO ZEN TAG +$MY_PATH/tools/natools.py encrypt -p $G1PUB -i ~/.ipfs/keystore/${keyfilename} -o ~/.zen/tag/${AAH}/_ipfs.publishkey.crypt +# LOG +echo " +${AA}.key +CYPHERING PUBLISH KEY + __ _ + _/_/ __/|_ __/|_ | | + / / | / | / / / + / / /_ __| /_ __| / / +/ / |/ |/ _/_/ +|_| /_/ + +KEY : $BB +BBH : $BBH +" +####################################################################### +# PASSENGER FILE is added to IPFS (then link is cyphered: NO!) +# https://beechat.network/how-beechats-encryption-works/ +# SECURITY IS OBSOLETE AS WE ARE SHARING COPY WITH FRIENDS ;) +# ACTIVATE ZEN CONTRACT... 100 readers impresari +# LOVE ECONOMY IS FREE. EVERYBODY IS DONATING TO THE OTHERS NOW. +# INCOME != MONEY FORGE (Realtive Moey Theory applies HERE) +# CODE WILL BE CERTIFIED AS IPFS HASH. +if [[ -f $PASSENGER ]]; then + +echo " + _ _ _ _ _ _ _ _ _ + / \ / \ / \ / \ / \ / \ / \ / \ / \ +( p | a | s | s | e | n | g | e | r ) + \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ + +" + # SEARCH FOR SAME SOURCE in IPFS... Not to make it twice... + CHECKSWARM=$(grep -Rwl "$ZENSOURCE" ~/.zen/ipfs_swarm/.12D3KooW*/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/') + [[ $CHECKSWARM == "" ]] && CHECKSWARM=$(grep -Rwl "$ZENSOURCE" ~/.zen/ipfs/.$IPFSNODEID/TAG/*/_tag.zensource | tail -n 1 | cut -f 6 -d '/') + [[ -s ~/.zen/ipfs/.$IPFSNODEID/TAG/$ZENSOURCE/_tag.issuer ]] && ISSUERSWARM=$(cat ~/.zen/ipfs/.$IPFSNODEID/TAG/$ZENSOURCE/_tag.issuer) + [[ "$CHECKSWARM" != "" && "$ISSUERSWARM" == "$ISSUER" ]] && ipfs key rm $keyname && rm -Rf ~/.zen/tag/${AAH} \ + && echo "$ZENSOURCE ALREADY COPIED IN IPFS SWARM. $CHECKSWARM CANCEL" && exit 1 + # NO DUPLICATE + + echo "ADDING TO IPFS.................$(date)" + # ADD PASSENGER TO IPFS # COULD BE SUPER LONG !!! + IPASSENGER=$(ipfs add -q "$PASSENGER" -w | tail -n 1) + # TODO COMPARE DISK WRITING SPEED AND AVAILABLE SPACE TO CHOOSE BEST IN SWARM + echo "$(date) ............... YES /ipfs/$IPASSENGER" + + echo "$IPASSENGER" > ~/.zen/tag/${AAH}/_passenger.ipfs + # GET FILE NAME + PASSENGERNAME=$(basename -- "$PASSENGER") + echo "$PASSENGERNAME" > ~/.zen/tag/${AAH}/_passenger.filename + + # G1SSB NODE ACCESS + $MY_PATH/tools/natools.py encrypt -p $G1PUB -i ~/.zen/tag/${AAH}/_passenger.ipfs -o ~/.zen/tag/${AAH}/_passenger.ipfs.crypt + # BB OWNER ACCESS + openssl aes-256-cbc -pbkdf2 -k "$BB" -salt -in ~/.zen/tag/${AAH}/_passenger.ipfs -out ~/.zen/tag/${AAH}/_passenger.ipfs.BB.aes + + # CLEAN CLEARTEXT IPFS link ? LATER MAYBE + rm ~/.zen/tag/${AAH}/_passenger.ipfs + + # ZEN ECONOMY: ZEN READ payment + PARK rental + echo "$READ" > ~/.zen/tag/${AAH}/_passenger.read + echo "$PARK" > ~/.zen/tag/${AAH}/_passenger.park + +echo " +_passenger.filename : $PASSENGERNAME +_passenger.ipfs : $IPASSENGER +http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME + +# #### ## ##### ###### ##### +# # # # # # # # # # +# # # # # # # ##### # # +# # # ###### # # # # # +# # # # # # # # # # +###### #### # # ##### ###### ##### + +################################################################## +# DECODE AND READ _passenger.ipfs with BB Passphrase +################################################################## +openssl aes-256-cbc -pbkdf2 -k \"$BB\" -d -salt -in ~/.zen/tag/${AAH}/_passenger.ipfs.BB.aes -out /tmp/_passenger.ipfs +ipfs get -o /tmp \"/ipfs/$IPASSENGER/$PASSENGERNAME\" && vlc \"/tmp/$PASSENGERNAME\" 2>/dev/null + +" + +cat > ~/.zen/tag/${AAH}/CONTRACT.sh < ~/.zen/tag/${AAH}/_chain.n # Tag modification number (0 first) + +NANODATE=$(date -u +%s%N) +echo "$NANODATE" > ~/.zen/tag/${AAH}/_chain.nanodate # Nanodate notification + +I0=$(ipfs add -qr ~/.zen/tag/${AAH} | tail -n 1) +echo "${I0}" > ~/.zen/tag/${AAH}/_chain + +# Double IPFS add for _chain INIT +cp -f ~/.zen/tag/${AAH}/_chain ~/.zen/tag/${AAH}/_chain.prev +I=$(ipfs add -qr ~/.zen/tag/${AAH} | tail -n 1) +echo "${I}" > ~/.zen/tag/${AAH}/_chain + +echo " + _|_ _.o._ +(_| |(_||| | : $NANODATE + +J0=${J0} +I0=${I0} +I=${I} +" + +# IPNS name publish +J=$(ipfs name publish -k $keyname --quieter /ipfs/${I}) +[[ ! ${J} ]] && echo "ipfs pin rm ${I}; ipfs pin rm ${I0}; rm -Rf ~/.zen/tag/${AAH}; ipfs key rm $keyname " +[[ ! ${J} ]] && J=${J0} + +echo "${J}" > ~/.zen/tag/${AAH}/_ipns +# INDEXING ZenTag into SWARM +mkdir -p ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/ +echo "${AA}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.uid +echo "${ZENSOURCE}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.zensource +echo "${ISSUER}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.issuer +echo "${J}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.ipns + +if [[ "${PASSENGERNAME}" != "" ]]; then + echo "${PASSENGERNAME}" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.filename + # COPY METADATA!! TODO : Please Extend filesource FOR new metadata types. torrent ? + extractor=$(cat "$METADATA" | jq -r '.extractor') + + youtubeid=$(cat "$METADATA" | jq -r '.id') + fulltitle=$(cat "$METADATA" | jq -r '.fulltitle') + echo "$fulltitle" > ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.fulltitle +# description=$(cat "$METADATA" | jq -r '.description') + artist=$(cat "$METADATA" | jq -r '.artist') + album=$(cat "$METADATA" | jq -r '.album') + duration=$(cat "$METADATA" | jq -r '.duration') + upload_date=$(cat "$METADATA" | jq -r '.upload_date') + uploader_id=$(cat "$METADATA" | jq -r '.uploader_id') + + extractor=$(cat "$METADATA" | jq -r '.extractor') + [[ ! -f ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.metadata.${extractor}.json ]] \ + && cp -f "${METADATA}" /tmp/ + mv /tmp/${youtubeid}.info.json ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/_tag.passenger.metadata.${extractor}.json +fi +ls ~/.zen/ipfs/.$IPFSNODEID/TAG/${ZENSOURCE}/ + +IWALLETS=$(ipfs add -rHq ~/.zen/ipfs | tail -n 1) +NODEIPNS=$(ipfs name publish --quieter /ipfs/$IWALLETS) + +echo " + _ ____ + (_)___ / __/____ + / / __ \/ /_/ ___/ + / / /_/ / __(__ ) +/_/ .___/_/ /____/ + /_/ +ipfs ls /ipfs/${I} + +ZenTAG : ipfs ls /ipns/${J} +NODE index : ipfs ls /ipns/$IPFSNODEID/.$IPFSNODEID/TAG/${ZENSOURCE} +" + +######################################################################## +# CREATE ZenTAG READ/WRITE QRCODES +### PRINTER IS HOOKED TO G1SSB NODE = G1DAB +######################################################################## +echo " + _ _ _ _ _ _ +/ \|_) / / \| \|_ +\_X| \ \_\_/|_/|_ ................................. +" +# READ QRCODE +qrencode -s 5 -o ~/.zen/tag/${AAH}/_QRCODE.read.png "RJ:${AA}#${J}" +# WRITE QRCODE +qrencode -s 5 -o ~/.zen/tag/${AAH}/_QRCODE.write.png "BJ:${BB}#${J}" +## TODO PROD REMOVE WRITE FILE + +echo "QRCodes CREATED !! +See it : +xviewer ~/.zen/tag/${AAH}/_QRCODE.read.png & +xviewer ~/.zen/tag/${AAH}/_QRCODE.write.png & + +${AA} +" + +[[ $(which xviewer) ]] && xviewer ~/.zen/tag/${AAH}/_QRCODE.read.png & +[[ $(which xviewer) ]] && xviewer ~/.zen/tag/${AAH}/_QRCODE.write.png & + + +######################################################################## +# SBOT PUBLISH +######################################################################## +if [[ "${PASSENGERNAME}" != "" ]]; then + echo "$ISSUER" + msg="$(sbotc get '{"id":"'"$ISSUER"'"}')" + # echo "$msg" | jq #DEBUG + [[ $msg == "" ]] && echo "ERROR No SSB message for PASSENGER (timestamp: $tstamp)" && exit 1 + msg_root=$(printf %s "$msg" | jq -r .value.content.root) + [[ $msg_root == "null" ]] && msg_root=$ISSUER + msg_branch=$(printf %s "$msg" | jq -r .value.content.branch) + [[ $msg_branch == "null" ]] && msg_branch=$ISSUER + + # ATTACH ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg + name="ssb_thumb.jpg" + id="$(sbotc blobs.add < ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)" + type="$(file -b --mime-type ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)" + size="$(wc -c < ~/.zen/miam/$ZENSOURCE/ssb_thumb.jpg)" + + export MESSAGE=$(cat << EOF +[![ssb_thumb.jpg](${id})](http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME) + +:extractor:${extractor} +:fulltitle:${fulltitle} +:duration:${duration} +:uploader_id:${uploader_id} + +[:pig:IPFS:pig_nose:](http://127.0.0.1:8080/ipfs/$IPASSENGER/$PASSENGERNAME) + +--- +**ZEN=$ZEN($READ/$PARK)** +[:flying_saucer:Essaim IPFS:sparkle:](http://127.0.0.1:8080/ipns/${J}/CONTRACT.sh) + +[:sunglasses:**ZenTAG**:heart:DEBUG:pager:](http://127.0.0.1:8080/ipns/$IPFSNODEID/.$IPFSNODEID/TAG/${ZENSOURCE}) + +#zenbot +#astroport +EOF +) + +echo " + __ _ ____ +(_ |_)/ \| +__)|_)\_/| POST + +root: $msg_root +branch: $msg_branch +" +echo "$MESSAGE" +INLINE=$(node -p "JSON.stringify(process.env.MESSAGE)") +### TODO find sboyc text COMMA PROBLEM... Cannot send beautiful md formated SSB messages !!! +# echo '{"type":"post", "branch": "'"$msg_branch"'", "root": "'"$msg_root"'", "text":"'"${INLINE}"'","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":"'"$size"'","type":"'"$type"'"},{"link":"#zenbot"},{"link":"#astroport"}]}' +sbotc publish '{"type":"post", "branch": "'"$msg_branch"'", "root": "'"$msg_root"'", "text":'${INLINE}',"mentions":[{"link":"'"$id"'","name":"'"$name"'","size":"'"$size"'","type":"'"$type"'"},{"link":"#zenbot"},{"link":"#astroport"}]}' + + +else +# This code is activated for qrcode (zentx terminal) @piniche +# REGULAR ZenTAG, Send QRCode to INPUT TX emitter +### TODO PRINT AND SEND BY SSB WITH WHEN PASSENGER TOO +# CAREFULL IN THAT CASE ISSUER IS A G1WALLET +ssbid="@$(echo $ISSUER | base58 -d | base64).ed25519" + +file=~/.zen/tag/${AAH}/_QRCODE.read.png +#recps='@f/6sQ6d2CMxRUhLpspgGIulDxDCwYD7DzFzPNr7u5AU=.ed25519 @5XaVcAJ5DklwuuIkjGz4lwm2rOnMHHovhNg7BFFnyJ8=.ed25519' +recps="$ssbid" + +name=${file##*/} +link="$(sblob encrypt "$file")" +type="$(file -b --mime-type "$file")" +id=${link%?unbox=*} +key=${link#*?unbox=} +size="$(sbotc -e blobs.size "$id")" +recps_array="[$(for recp in $recps; do printf '"%s"\n' "$recp"; done | paste -sd,)]" +sbotc private.publish '{"type":"post","text":"['"$name"']('"$link"')","mentions":[{"link":"'"$id"'","name":"'"$name"'","size":'"$size"',"type":"'"$type"'","query":{"unbox":"'"$key"'"}}],"recps":'"$recps_array"'}' "$recps_array" + + +fi + +######################################################################## +exit 0 diff --git a/zen/zen_OP.sh b/zen/zen_OP.sh new file mode 100755 index 0000000..c423768 --- /dev/null +++ b/zen/zen_OP.sh @@ -0,0 +1,98 @@ +#!/bin/bash +################################################################################ +# Author: Fred (support@qo-op.com) +# Version: 1.0 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +########################################################################################### +# tag_OP.sh OPERATION ON ZenTAG... + +########################################################################################### +# fred@ONELOVE:~/workspace/ZEN$ ipfs ls /ipns/QmZxwgScLyLjitE1mrUFqpTw27gk6tMdfKQ7QTEo28dE2u +# QmVkur97Pqpgt7cy71FCNacPd5Z1eD9QVfw5tu4LdXHavk 47 _chain +# QmUQcSjQx2bg4cSe2rUZyQi6F8QtJFJb74fWL7D784UWf9 2 _chain.n +# QmRPdapkSVeYeRcC6X7freVo8vrGYjKcGsa96V5GsMg36p 20 _chain.nanodate +# Qmc5m94Gu7z62RC8waSKkZUrCCBJPyHbkpmGzEePxy2oXJ 1 _chain.prev +# Qmb8gYFmno4QeEpaPTi969qQNyVJK9VyfBbsXtyMA3PMow 45 _g1.creator +# QmQo6ghBGdkGCnGoB22cYmraR76q44K1mjVXqQQ3UVn2T1 45 _g1.issuer +# QmfUFk27ZvHaGfEwBeqResw2qQ9SCu4tsY69SkVuCdWAxB 65 _g1.txhash +# QmaKa7FGwmMshE7RACrWAFCsWETu1HcHnV1eak7HcUk6Cs 47 _ipfs.creator +# QmfCL14hnVnm3mrYT7fRjveMRTEoSp2axqCVp3JYoxXXgn 1300 _ipfs.publishkey.B.gpg +# QmUQ7WsACNYYWAw4QW8hKTCq8MM8oYGV7HJuxW7QTGj9qS 1243 _ipfs.publishkey.crypt +# QmdvRknZWSZUyS9SfmFWcvySv1tgi2PTF711rsGPN6ftL1 65 _tag.B.sha +# QmXA1zSxA3dh1dxFtkKaH5iomN3BsKMSAo2qhruA58AU81 34 _tag.uid +# QmPrumas2N5DuwcqUz9mayAU6oDL2bLjhpkVZ8hCRq9rpx 5 _tag.zen +############################################################################################ +# TRANSFERT $VALUE ZEN FROM $JSOURCE ZenTag to $JDEST ZenTag +JSOURCE=$1 +JDEST=$2 +VALUE=$3 + +IPFSNODEID=$(ipfs id -f='\n') +NANODATE=$(date -u +%s%N) +############################################ +echo "############################################ +zen_OP.sh ZenTag TRANSFER $NANODATE +############################################ +SOURCE(1): $JSOURCE +DEST(2): $JDEST +VALUE(3): $VALUE ZEN +############################################" + +############################################################################################################# +# SOURCE +############################################################################################################# +############## SOURCE EXTRACTION +JSOURCEUID=$(ipfs cat /ipns/$JSOURCE/_tag.uid) +JSOURCERR=$(echo "$JSOURCEUID" | sed s/\ //g) +############################################# +############## GOOD or BAD ? +[[ $JSOURCERR == "" ]] && echo "NO G1Tag /ipns/$JSOURCE ... Ciao!" && exit +[[ ! -d /tmp/$JSOURCERR ]] && mkdir -p /tmp/$JSOURCERR || rm -f /tmp/$JSOURCERR/* +ipfs get --output=/tmp/${JSOURCERR}/ /ipns/$JSOURCE > /dev/null 2>&1 +# echo "Retrieving SOURCE G1Tag: ipfs get --output=/tmp/${JSOURCERR}/ /ipns/$JSOURCE" + +JSOURCECREATOR=$(cat /tmp/${JSOURCERR}/_ipfs.creator) +JSOURCEVALUE=$(cat /tmp/${JSOURCERR}/_tag.zen) + +############################################# +############## No JDEST, then return $JSOURCEVALUE +[[ "$JDEST" == "" ]] && echo "ZenTAG ($JSOURCEUID) VALUE = $JSOURCEVALUE Zen" && exit +############################################# + +############################################# +############## TEST IF $VALUE IS AVAILABLE +FINALSOURCE=$(echo "${JSOURCEVALUE} - ${VALUE}" | bc -l) +[[ $FINALSOURCE -lt 0 ]] && echo "Manque $FINALSOURCE Zen à ce ZenTAG... Ciao!" && exit + +############################################################################################################# +# DEST +############################################################################################################# +############## DESTINATION EXTRACTION +JDESTUID=$(ipfs cat /ipns/$JDEST/_tag.uid) +JDESTRR=$(echo "$JDESTUID" | sed s/\ //g); +############################################# +############## GOOD or BAD ? +[[ $JDESTRR == "" ]] && echo "NO G1Tag /ipns/$JDEST ... Ciao!" && exit +[[ ! -d /tmp/$JDESTRR ]] && mkdir -p /tmp/$JDESTRR || rm -f /tmp/$JDESTRR/* +ipfs get --output=/tmp/$JDESTRR/ /ipns/$JDEST > /dev/null 2>&1 + +JDESTCREATOR=$(cat /tmp/${JDESTRR}/_ipfs.creator) +JDESTVALUE=$(cat /tmp/${JDESTRR}/_tag.zen); + +FINALDEST=$(echo "${JDESTVALUE} + ${VALUE}" | bc -l) + +###################################################################################################### +# CHECK NANODATE +LAST=$(cat /tmp/${JSOURCERR}/_chain.nanodate) +timediff=$( echo "${NANODATE} - ${LAST}" | bc -l ) +# NODE TIME SYNC 600 milliards de nanosecondes = 600 s = 10 mn +if [[ $timediff -lt 600000000000 ]]; then + echo "LAST OPERATION NOT FINISHED YET... RETRY IN A FEW MINUTES" + exit +fi + +echo "OK. OPERATION ALLOWED... +$JSOURCERR (/ipns/$JSOURCE) : $JSOURCEVALUE => $FINALSOURCE +$JDESTRR (/ipns/$JDEST) : $JDESTVALUE => $FINALDEST" + +# TODO: SEND TASK TO $JSOURCECREATOR AND $JDESTCREATOR diff --git a/zen/zen_PASSENGER_READ.sh b/zen/zen_PASSENGER_READ.sh new file mode 100755 index 0000000..4ffc1d9 --- /dev/null +++ b/zen/zen_PASSENGER_READ.sh @@ -0,0 +1,72 @@ +#!/bin/bash +######################################################################## +# Author: Fred (support@qo-op.com) +# Version: 2020.03.20 +# License: AGPL-3.0 (https://choosealicense.com/licenses/agpl-3.0/) +######################################################################## +MY_PATH="`dirname \"$0\"`" # relative +MY_PATH="`( cd \"$MY_PATH\" && pwd )`" # absolutized and normalized +ME="${0##*/}" +echo ' +######################################################################## +# \\/// +# qo-op +############# '$ME' QREAD QWRITE +######################################################################## +# ex: ./'$ME' RJ:AAH#JR BJ:BB#JW +# RQR=RAAH#JR PASSENGER ZenTag read with WQR=BB#JW ZenTag +######################################################################## + + /| + = = = / | + ____| || || |____/ | -_-_-_-_-_-_ +|)----| || || |____ | AH + (( | || || | ))\ | _-_-_-_-_-_- + \\_|_||_||_|_// \ | + \___________/ \| + +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +# [ASTROPORT](https://astroport.com) +######################################################################## +' +QREAD="$1" +QWRITE="$2" +############################################## +# NODE ENVIRONEMENT DETECTION +############################################## +ipfsnodeid=$(ipfs id -f='\n') +[[ ! -f ~/.ssb/secret.dunikey ]] && $MY_PATH/tools/secret2dunikey.sh +g1pub=$(cat ~/.ssb/secret.dunikey | grep 'pub:' | cut -d ' ' -f 2) + +############################################################### +# ZenTag READ Passenger with ZenTag WRITE + +# Check ZenTag +[[ $(echo $QREAD | cut -d ":" -f 1) != "RJ" ]] && echo "ERROR BAD $QREAD" && exit 1 +[[ $(echo $QWRITE | cut -d ":" -f 1) != "BJ" ]] && echo "ERROR BAD $QWRITE" && exit 1 + +# Get ZenTag READ +RQR=$(echo $QREAD | cut -d ":" -f 2) +RAAH=$(echo $RQR | cut -d "#" -f 1) +if [[ ! -d ~/.zen/tag/$RAAH ]]; then + mkdir -p ~/.zen/tag/$RAAH + RJ=$(echo $RQR | cut -d "#" -f 2) + ipfs get -o ~/.zen/tag/$RAAH /ipns/$RJ +fi + +PASSENGERNAME=$(cat ~/.zen/tag/$RAAH/_passenger.filename) +READPRICE=$(cat ~/.zen/tag/$RAAH/_passenger.read) + + +# Get ZenTag WRITE +WQR=$(echo $QWRITE | cut -d ":" -f 2) +BB=$(echo $WQR | cut -d "#" -f 1) +WJ=$(echo $WQR | cut -d "#" -f 2) +if [[ ! -d ~/.zen/tag/$RAAH ]]; then + mkdir -p ~/.zen/tag/$RAAH + + ipfs get -o ~/.zen/tag/$RAAH /ipns/$RJ +fi + + +$MY_PATH/shell/tag_OP.sh ${obj[1]} ${obj[0]} $COMBIENZEN "$SWARM_G1AUTHFILE"