def mstdn_name_update(status): mastodon = Mastodon(access_token="key/username-updater-usercred.secret", api_base_url="https://mstdn.maud.io") with open(PATH + "/mstdn-id.txt", "r") as f: id = int(f.read()) display_name = mastodon.account(id)["display_name"][:-2] mastodon.account_update_credentials(display_name=display_name + status)
class MastodonAccountEditor(AccountEditor): def __init__(self): super().__init__() initialize_mastodon() self.server_url = config("MASTODON_SERVER") mastodon_data_path = Path(__file__).parents[2] / "data" / "mastodon" self.mastodon = Mastodon( client_id=(mastodon_data_path / "my_clientcred.txt").as_posix(), access_token=(mastodon_data_path / "my_usercred.txt").as_posix(), api_base_url=self.server_url) self.name = self.mastodon.account_verify_credentials()['username'] def post_name(self, name): super().post_name(name) self.mastodon.account_update_credentials(display_name=name)
#!/usr/bin/python3 from mastodon import Mastodon import musicpd port = 6600 host = 'localhost' instance = 'https://cybre.space' client = musicpd.MPDClient() client.connect(host, port) now = client.currentsong() client.close() client.disconnect() nowp = ("Now Playing: %s - %s" % (now['title'], now['artist'])) print(nowp) mast = Mastodon(client_id='/home/USER/.ncmpcpp/masto-muse_appcred.secret', access_token='/home/USER/.ncmpcpp/masto-muse_usercred.secret', api_base_url=instance) mast.account_update_credentials(note=nowp)
class MoonBot(): def __init__(self, cache_dir=None): #self.maxchar = 280 #twitter maxchar self.light_gradient = [str(x).zfill(2) for x in range(100)] self.cache_dir = cache_dir self.astrology = False def set_modes(self, gradient_symbols="moon2", astrology=False): self.gradient_symbols = gradient_symbols self.settings = settings_dict.settings[gradient_symbols] self.astrology = astrology self.charwidth = self.settings["ascii_dims"][0] self.charheight = self.settings["ascii_dims"][1] self.ascii_gradient = [''] * (self.charwidth * self.charheight) def decide_new_moon_border(self): #if True:#self.luminosity <= .1 and self.settings["settings_type"] == "random": if self.moon.moon_datetime_info[ "age"] < 3.0 or self.moon.moon_datetime_info["age"] > 25.0: self.add_border = True surrounding_pixels_offsets = { "char_to_left": -1, "char_to_right": 1, "char_above": -1 * self.charwidth, "char_below": self.charwidth, "char_up_right": (-1 * self.charwidth) + 1, "char_up_left": (-1 * self.charwidth) - 1, "char_down_right": self.charwidth + 1, "char_down_left": self.charwidth - 1 } #make a shadow border for idx, value in enumerate(self.result_moon_gradients): #check whether there's a border character adjacent to this one border_char_adjacent = -1 in [ self.result_moon_gradients[idx + offset] for offset in surrounding_pixels_offsets.values() if idx + offset >= 0 and idx + offset <= (self.charwidth * self.charheight) - 1 ] if border_char_adjacent: continue for desc, offset in surrounding_pixels_offsets.items(): if idx + offset >= 0 and idx + offset <= ( self.charwidth * self.charheight ) - 1: #ignore negative offsets as they're at the end of the array and not nearby try: if int(self.result_moon_gradients[ idx + offset]) == 0 and int( self.result_moon_gradients[idx]) > 0: self.result_moon_gradients[idx + offset] = -1 except ValueError: pass except IndexError: pass #print(self.result_moon_gradients) def choose_random_shadow_symbol(self): return random.choice(self.settings["symbols"]["random_shadow_symbols"]) def choose_random_light_symbol(self): return random.choice(self.settings["symbols"]["random_light_symbols"]) def make_ascii_tweet(self): #pdb.set_trace() self.convert_image_to_ascii(self.charwidth, 1) #self.decide_new_moon_border() if self.astrology: self.set_astrology_info() self.set_symbols_list(self.luminosity) self.put_symbols_in_gradient() self.put_output_in_square() self.make_img_to_post() def put_symbols_in_gradient(self): self.ascii_list = [] if self.astrology: #find which luminances have just one or two self.least_often_gradient_value1 = Counter( self.result_moon_gradients).most_common()[-1][0] self.least_often_gradient_value2 = Counter( self.result_moon_gradients).most_common()[-2][0] for value in self.result_moon_gradients: if value == self.least_often_gradient_value1: self.ascii_list.append(self.astrology_sign_random_emoji) elif value == self.least_often_gradient_value2: self.ascii_list.append(self.astrology_element_random_emoji) elif value == -2: self.border_char = "💡" #$todo self.ascii_list.append(self.border_char) elif value == -1: self.border_char = "🌚" #$todo self.ascii_list.append(self.border_char) else: self.ascii_list.append(self.ascii_gradient[int(value)]) def make_img_to_post(self): #In-stream images are displayed at a 16:9 ratio of 600px by 335px # and can be clicked and expanded up to 1200px by 675px. font_size = 80 width = DIMS[0] height = DIMS[1] twitter_im_width = 800 twitter_im_height = 450 bg_color = (0, 0, 0) font_size = int(width / self.charwidth) unicode_text = self.ascii im = Image.open("moon.jpg") moon_waning = self.moon.moon_datetime_info["age"] > 14 #im = Image.new ( "RGB", (width,height), bg_color ) unicode_font = ImageFont.truetype( "res/unicode-emoji/symbola/Symbola.ttf", font_size) draw = ImageDraw.Draw(im) for x in range(0, self.charwidth): for y in range(0, self.charheight): lum = int(255 - 255 / (int(self.result_moon_gradients[ (x * self.charwidth) + y]) + 1)) if type(lum) == type(1.0): raise ValueError("float ", lum) if self.astrology == True: if self.moon_sign in ["Sagittarius", "Leo", "Aries"]: #red, yellow r = 255 g = lum b = int(255 - (255 / (lum + 1) * (moon_waning * x * self.charwidth + y + 1))) elif self.moon_sign in ["Taurus", "Capricorn", "Virgo"]: #green, blue r = lum g = 255 b = int(255 - (255 / (lum + 1) * (moon_waning * x * self.charwidth + y + 1))) elif self.moon_sign in ["Gemini", "Libra", "Aquarius"]: #blue, purple r = lum g = int(255 - (255 / (lum + 1) * (moon_waning * x * self.charwidth + y + 1))) b = 255 elif self.moon_sign in ["Pisces", "Cancer", "Scorpio"]: #blue, green r = 255 g = int(255 - (255 / (lum + 1) * (moon_waning * x * self.charwidth + y + 1))) b = lum else: #todo make an rgb that looks good r, g, b = (lum, lum, lum) if r > 255 or g > 255 or b > 255: raise ValueError( f"One of your RGB colors is higher than 255, your lum is: {lum}" ) font_color = (r, g, b) draw.text((x * int(width / self.charwidth), y * int(width / self.charheight)), self.ascii_list[(x * self.charwidth) + y], font=unicode_font, fill=font_color) #draw.text ( (0,0), self.moon_sign, font=unicode_font, fill=(255,255,0) ) #self.ascii += str(self.ascii_list[(y * self.charwidth) + x]) #im = Image.new ( "RGB", (width,height), bg_color ) background_im = Image.new("RGB", (twitter_im_width, twitter_im_height), bg_color) draw = ImageDraw.Draw(background_im) offset = ((twitter_im_width - width) // 2, (twitter_im_height - height) // 2) background_im.paste(im, offset) background_im.show() background_im.save("moon_emojis.png") #.jpg makes problems def calculate_luminosity(self): self.average_luminosity = 1 def make_histogram(self): self.hist = plt.hist(self.moon.image.ravel(), 256, [0, 256]) def put_output_in_square(self): self.ascii = "" for x in range(0, self.charwidth): for y in range(0, self.charheight): self.ascii += str(self.ascii_list[(y * self.charwidth) + x]) self.ascii += "\n" def twitter_signin(self): auth = tweepy.OAuthHandler(TWITTER_CONSUMER_KEY, TWITTER_CONSUMER_SECRET) auth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET) auth.set_access_token(TWITTER_ACCESS_TOKEN, TWITTER_ACCESS_TOKEN_SECRET) self.twitter_api = tweepy.API(auth, parser=tweepy.parsers.JSONParser()) def mast_signin(self): self.mast_api = Mastodon(client_id=MAST_CLIENT_KEY, client_secret=MAST_CLIENT_SECRET, api_base_url='https://botsin.space', access_token=MAST_ACCESS_TOKEN) def post_moon_toot(self): FPATH = "" data = {} try: image = FPATH + "moon_emojis.png" media = self.mast_api.media_post(image, description=self.alt_text) status = self.mast_api.status_post(self.moon_info_caption, media_ids=media, sensitive=False) except Exception as e: raise e #TODO use python mastodon api port or figure out why this isn't working def mast_update_profile_image(self): # with open('moon.jpg', 'rb') as f: # image = f.read() account = self.mast_api.account_update_credentials(avatar="moon.jpg") def get_moon(self, **kwargs): try: self.moon = MoonImage() if not self.cache_dir: self.moon.set_moon_phase(**kwargs) else: #check if this moon time exists in cached img dir if not os.path.isdir(self.cache_dir): raise OSError( (f"Can't find a directory called " "'{self.cache_dir}'. Please double " "check that you've created this directory.")) self.moon.set_moon_datetime(**kwargs) date_filename = (f'{self.cache_dir}' f'{self.moon.datetime.year}-' f'{self.moon.datetime.month}-' f'{self.moon.datetime.day}-' f'{self.moon.datetime.hour}') if os.path.isfile(date_filename + '.jpg'): # TODO would be nice if `dialamoon.Moon` would do this in Moon.set_moon_phase() self.moon.image = cv2.imread(date_filename + '.jpg') self.moon.resize_image() self.set_up_cached_json() else: self.moon.set_moon_phase(**kwargs) self.moon.save_to_disk(date_filename) #self.moon.image = cv2.resize(self.moon.image, DIMS) self.moon.image = cv2.resize(self.moon.image, DIMS) #self.moon.image = cv2.resize(self.moon.image, (200,200)) cv2.imwrite("moon.jpg", self.moon.image) except Exception as e: raise e def set_symbols_list(self, luminosity): #TODO clean up so it isn't so rickety try: if self.settings["settings_type"] == "map": if self.moon.moon_datetime_info["age"] < 14: intervals = self.settings["intervals"]["young_moon"] else: intervals = self.settings["intervals"]["old_moon"] i = 0 for n in sorted(intervals.keys()): rep = n - i self.ascii_gradient[i:n + 1] = [intervals[n]] * rep i = n else: if self.moon.moon_datetime_info["age"] < 14: self.ascii_gradient = stretch( self.settings["symbols"]["young_moon"], self.charwidth * self.charheight) else: self.ascii_gradient = stretch( self.settings["symbols"]["old_moon"], self.charwidth * self.charheight) if self.settings["settings_type"] == "random": self.ascii_gradient = self.set_random_chars() except TypeError as e: raise TypeError( f'Something might be wrong with your settings_dict {self.gradient_symbols} mode\n' + str(e)) def set_astrology_info(self): d = Datetime( f'{self.moon.datetime.year}/{self.moon.datetime.month}/{self.moon.datetime.day}', f'{self.moon.datetime.hour}:00', '+00:00') pos = GeoPos( '38n32', '8w54') #todo use a different location? this was in the docs self.chart = Chart(d, pos) self.moon_astrology_info = self.chart.get(const.MOON) self.moon_sign = self.moon_astrology_info.sign self.astrology_ascii_dict = astrology_dict["signs"][self.moon_sign] self.astrology_sign_random_emoji = choice( astrology_dict["signs"][self.moon_sign]["related"]) self.astrology_element_random_emoji = choice( astrology_dict["elements"][self.astrology_ascii_dict["element"]]) # get aspects of chart dyn = ChartDynamics(self.chart) self.aspects = dyn.validAspects(const.MOON, const.MAJOR_ASPECTS) def set_up_cached_json(self): #TODO do this in dialamoon.Moon some day json_path = self.cache_dir + JSON_FN.format( year=self.moon.datetime.year) if not os.path.isfile(json_path): self.moon.make_json_year_data_url() self.moon.set_json_year_data() with open(json_path, 'w') as outfile: json.dump(self.moon.moon_year_info, outfile) else: with open(json_path, 'r') as outfile: s = outfile.read() if s == "": raise ValueError( f"The {json_path} file is empty. Please try again.") self.moon.moon_year_info = json.loads(s) self.moon.set_json_specific_data() def set_moon_info_caption(self): self.moon_info_caption = "...\n\n" + str( self.moon.moon_datetime_info["distance"]) + "km from earth".rjust( 22, " ") + "\n" + str(self.moon.moon_datetime_info["phase"] ) + "%" + "illuminated".rjust( 26, " ") + "\n\n" def set_alt_text(self): self.alt_text = f"Image of the moon, {self.moon.moon_datetime_info['phase']}% illuminated, with emojis overlaid" def post_moon_tweet(self): media = self.twitter_api.media_upload("moon_emojis.png") #if .create_media_metadata makes a problem, it may be because tweepy.parser.JSONParser #is receiving '' as payload (from resp.text) and json.loads() errors r = self.twitter_api.create_media_metadata( media_id=media["media_id_string"], alt_text=self.alt_text) self.twitter_api.update_status(self.moon_info_caption, media_ids=[media["media_id_string"]]) def update_profile_image(self): self.twitter_api.update_profile_image("./moon.jpg") # START pixelating grayscale image with characters/emojis ### PYTHON TO ASCII ART - https://github.com/electronut/pp/blob/master/ascii/ascii.py#L2 modified to just take numpy images def convert_image_to_ascii(self, cols, scale): """ Given Image and dims (rows, cols) returns an m*n list of Images """ # declare globals # open image and convert to grayscale im = self.rgb_to_gray(self.moon.image) # store dimensions W, H = im.shape[0], im.shape[1] # compute width of tile w = W / cols # compute tile height based on aspect ratio and scale h = w / scale # compute number of rows rows = int(H / h) #why not get the average luminosity of the whole image first self.luminosity = self.getAverageL(im) / 100 # check if image size is too small if cols > W or rows > H: raise Exception("Image too small for specified cols!") exit(0) # ascii image is a list of character lists aimg = [] # generate list of dimensions for j in range(rows): y1 = int(j * h) y2 = int((j + 1) * h) # correct last tile if j == rows - 1: y2 = H # append an empty string aimg.append([]) for i in range(cols): # crop image to tile x1 = int(i * w) x2 = int((i + 1) * w) # correct last tile if i == cols - 1: x2 = W # crop image to extract tile img = im[x1:x2, y1:y2] # get average luminosity avg = int(self.getAverageL(img)) # look up value in light gradient gsval = self.light_gradient[int((avg * 99) / 255)] # append ascii char to string aimg[j].append(gsval) #transpose it as its currently rotated -90 deg #aimg = list(map(list, np.transpose(aimg))) # return light_gradients self.result_moon_gradients = [ item for sublist in aimg for item in sublist ] def rgb_to_gray(self, img): #make an array of zeros with the shape (1000, 1000, 3) # (1000 px by 1000 px each with 3 values - RGB) grayImage = np.zeros(img.shape) #take just the red, green and blue values from the 1000x1000 R = np.array(img[:, :, 0]) G = np.array(img[:, :, 1]) B = np.array(img[:, :, 2]) # ITU-R 601-2 luma transform coefficients for converting rgb > greyscale R = (R * .299) G = (G * .587) B = (B * .114) Avg = (R + G + B) grayImage = img for i in range(3): grayImage[:, :, i] = Avg return grayImage def getAverageL(self, im): """ Given PIL Image, return average value of grayscale value """ # get shape w = im.shape[0] h = im.shape[1] # get average #cv2.imshow("test", im) #cv2.waitKey(0) #cv2.destroyAllWindows() return np.average(im)
from settings import * def get_state_text(): try: json = requests.get(SPACE_API_URL).json() is_open = json['state']['open'] state_text = STATE_OPEN if is_open else STATE_CLOSED last_change = json['state'].get('lastchange') if last_change is not None: timestamp = datetime.fromtimestamp(last_change) return timestamp.strftime(state_text) else: return state_text except: return STATE_UNKNOWN mastodon = Mastodon(access_token=ACCESS_TOKEN_FILENAME, api_base_url=INSTANCE_URL) updated_fields = [] for field in mastodon.account_verify_credentials().fields: if field['name'] == STATE_FIELD_NAME: updated_fields.append((STATE_FIELD_NAME, get_state_text())) else: soup = BeautifulSoup(field['value'], 'html.parser') updated_fields.append((field['name'], soup.get_text())) mastodon.account_update_credentials(fields=updated_fields)
if heartbeat: for instance_data in config['instance_data_list']: domain = instance_data['domain'] username = instance_data['username'] bot_client_secret_fn = os.path.join('data', 'instances', domain, 'bot_client.secret') user_secret_file = os.path.join( 'data', 'accounts', common.md5('{0},{1}'.format(domain, username)), 'user.secret') api_base_url = 'https://{0}'.format(domain) mastodon = Mastodon(client_id=bot_client_secret_fn, access_token=user_secret_file, api_base_url=api_base_url) mastodon.account_update_credentials( display_name=config['detection_acc_display_name'], note=config['detection_acc_description']) for instance_data in config['report_acc_list']: domain = instance_data['domain'] username = instance_data['username'] bot_client_secret_fn = os.path.join('data', 'instances', domain, 'bot_client.secret') user_secret_file = os.path.join( 'data', 'accounts', common.md5('{0},{1}'.format(domain, username)), 'user.secret') api_base_url = 'https://{0}'.format(domain) mastodon = Mastodon(client_id=bot_client_secret_fn, access_token=user_secret_file, api_base_url=api_base_url) mastodon.account_update_credentials(