#!/usr/bin/env python3
message = '''
Sashanoraa
----------
Pronouns: any
Gender: Non-binary / Gender Queer
Matrix: @zethra:matrix.org
Email: ben@benaaron.dev
Mastodon: @zethra@fosstodon.org
Website: sashanoraa.gay
Protonmail: sashanoraa@protonmail.com
'''.splitlines()
import sys
import math
import random
import signal
import argparse
from time import sleep
COLOR_ANSI = (
(0x00, 0x00, 0x00), (0xcd, 0x00, 0x00),
(0x00, 0xcd, 0x00), (0xcd, 0xcd, 0x00),
(0x00, 0x00, 0xee), (0xcd, 0x00, 0xcd),
(0x00, 0xcd, 0xcd), (0xe5, 0xe5, 0xe5),
(0x7f, 0x7f, 0x7f), (0xff, 0x00, 0x00),
(0x00, 0xff, 0x00), (0xff, 0xff, 0x00),
(0x5c, 0x5c, 0xff), (0xff, 0x00, 0xff),
(0x00, 0xff, 0xff), (0xff, 0xff, 0xff),
)
class TextColor:
def __init__(self, mode=256):
self.mode = mode
self.os = random.randint(10, 25)
self.spread = 5.0
self.freq = 0.1
def _distance(self, rgb1, rgb2):
return sum(map(lambda c: (c[0] - c[1]) ** 2,
zip(rgb1, rgb2)))
def ansi(self, rgb):
r, g, b = rgb
if self.mode in (8, 16):
colors = COLOR_ANSI[:self.mode]
matches = [(self._distance(c, map(int, rgb)), i) for i, c in enumerate(colors)]
matches.sort()
color = matches[0][1]
return '3%d' % (color,)
else:
gray_possible = True
sep = 2.5
gray = 0
while gray_possible:
if r < sep or g < sep or b < sep:
gray = r < sep and g < sep and b < sep
gray_possible = False
sep += 42.5
if gray:
color = 232 + int(float(sum(rgb) / 33.0))
else:
color = sum([16]+[int(6 * float(val)/256) * mod
for val, mod in zip(rgb, [36, 6, 1])])
return '38;5;%d' % (color,)
def wrap(self, *codes):
return '\x1b[%sm' % (''.join(codes),)
def rainbow(self, freq, i):
r = math.sin(freq * i) * 127 + 128
g = math.sin(freq * i + 2 * math.pi / 3) * 127 + 128
b = math.sin(freq * i + 4 * math.pi / 3) * 127 + 128
return [r, g, b]
def apply_color(self, s):
output = ""
for i, c in enumerate(s):
rgb = self.rainbow(self.freq, self.os + i / self.spread)
output += ''.join([
self.wrap(self.ansi(rgb)),
c
])
return output
def generate(args):
with open('profile') as f:
img = f.read().splitlines()
titles = TextColor()
data = TextColor()
# Set the data color seed to something far away from the title seed so they look different
data.os = ((titles.os - 10 + 7) % 15) + 10
with open(args.output, 'w') as output:
output.write('\n')
for (i, m) in zip(img, message):
if ':' in m:
parts = m.split(':')
output.write(f' {i} {titles.apply_color(parts[0])}: {data.apply_color(":".join(parts[1:]))}\n')
else:
output.write(f' {i} {titles.apply_color(m)}\n')
for i in img[len(message):]:
output.write(f' {i}\n')
def main():
parser = argparse.ArgumentParser(description="Generate cool neofetch like card")
parser.add_argument('output', help='Path to output to')
parser.add_argument('-d', type=float, help='Run in daemon mode, regenerating output every N minutes')
args = parser.parse_args()
if args.d:
wait_time = int(args.d * 60)
signal.signal(signal.SIGINT, lambda s, f: sys.exit(0))
signal.signal(signal.SIGTERM, lambda s, f: sys.exit(0))
print(f'Generating fetch output in daemon mode every {args.d} minutes')
while True:
generate(args)
sleep(wait_time)
print('Updating fetch output')
else:
print('Generating fetch output')
generate(args)
if __name__ == '__main__':
main()