понедельник, 31 июля 2023 г.

RevenueCat, Google Play и App Store: как передать идентификатор пользователя



Отпускаю эту информацию на просторы интернета. Если вы, товарищи по несчастью, тоже мучаетесь над вопросом "я работаю с RevenueCat, как передать user identifier в google play subscriptions, чтобы он вернулся в нотификациях и API и я мог понять, кто создал subscription", делаем несколько простых телодвижений:

1) Передаем свой драгоценный идентификатор (UUID, например) в originalAppUserId параметр при создании новой подписки RevenueCat

2) В AppStore API и ServerNotifications получаем его незашифрованным в параметре appAccountToken


3) В Google Play API purchases.subscriptionsv2 получаем его зашифрованным в externalAccountIdentifiers - obfuscatedExternalAccountId

4) Для шифрования (чтобы получить то же самое, что и в obfuscatedExternalAccountId) используем примерно такое (пример на питоне):
base64.b64encode(codecs.decode(sha256("MY_IDENTIFIER".encode("ascii")).hexdigest(), 'hex')).decode('ascii')

суббота, 17 февраля 2018 г.

py2exe и PIL

Товарищи, на копание с этой штукой я потратила несколько прекрасных вечеров, которые могла бы провести на море с пивом или за просмотром высокоинтеллектуальных артхаусных фильмов. А решение оказалось просто до идиотизма.

Итак, если кто-то вдруг еще пользуется py2exe и хочет запаковать свою прекрасную программу на питоне в exe-шник, а у программы есть в зависимостях PIL или другой пакет, поставляемый в виде яйца (.egg), то перед сборкой нужно это яйцо распаковать. Находим яйцо в C:\Python34\Lib\site-packages (т.е. внутри папки установленых pip'ом питоньих библиотек). Тупо приписываем .zip в конец имени файла, тупо распаковываем его архиватором в ту же папку. Тупо запускаем py2exe и не менее тупо идем развлекаться всеми доступными способами, потому что это собственно все, что нам нужно!

пятница, 3 ноября 2017 г.

GeeTeeDee - database error

Вчера отказался открываться любимый менеджер задач GeeTeeDee. Сказал, что почему-то не может открыть базу данных, обращайтесь, дескать к разработчикам. Но похоже, что поддержка продукта прекращена уже давно (по крайней мере, обновлений не было уже пару лет), так что своими силами исправление ошибки выглядит так.

а) Находим файл с базой - у меня он обнаружился тут C:\Users\<%username%>\AppData\Local\Codea\GeeTeeDee\User Data

б) Открываем файл database.xml, видим там какую-то похабень вместо нормального xml.

в) Если нам повезло, то в той же папке видим database.xml.bak с нормальным содержимым.

г) Бэкапы - это хорошо. Заменяем содержимое основного файла бэкапом. Рыдаем от счастья.

вторник, 24 января 2017 г.

ActiveMQ Websocket - ERR_INSECURE_RESPONSE

Давненько сюда не писала, но тут такой повод чудный, полдня с ним имела половые отношения. Итак, у вас стоит ActiveMQ на сервере, и вы хотите с фронтенда вашего прекрасного сайта через сокет с ним соединиться, чтобы наваять себе, например, чатик. А браузер, зараза такая, вам и говорит: Error in connection establishment: net::ERR_INSECURE_RESPONSE

Говорит он, правда, вам это в том случае, если сайт у вас, как и положено, с https, и соединение с сокетом, соответственно, тоже через wss (а другого вам браузер и не разрешит в таком случае). Теперь надо, чтобы браузер доверился вашему mq, как родному. Для этого нужно сотворить для брокера activemq сертификат, основанный на сертификате домена (он же у вас есть, раз у вас https, правда?)

Итак, по пунктам.

1) sudo openssl pkcs12 -export -in <путь к сертификату> -inkey <путь к ключу> -certfile <путь к сертификату>-out testkeystore.p12

2) sudo /usr/bin/keytool -importkeystore -srckeystore testkeystore.p12 -srcstoretype pkcs12 -destkeystore <имя нового сертификата для mq>.jks -deststoretype JKS

Внимание, путь к keytool может быть другой! Если не нашелся по этому адресу, ищите по серверу. Если стоит java, то и keytool где-то там.

3) В конфиге activemq.xml : <sslContext keyStore="file:${activemq.conf}/<имя нового сертификата для mq>.jks" keyStorePassword="<пароль, который вы задали при создании сертификата>" />

Вуаля. Сохраняем конфиг, перезагружаем activemq, идем пить пиво.

понедельник, 5 января 2015 г.

#!/usr/bin/python: No such file or directory

Рассматриваю случай, когда вы редактировали в Windows (Notepad ++), а потом попытались запустить скрипт через ssh без указания интерпретатора (то есть не python test.py, а просто test.py), и он вам выдал No such file or directory.

1) Проверьте формат окончания строки, он должен быть юниксовый (Правка - Формат строк - Преобразовать...)

2) Кодировка файла - UTF-8 без BOM.

И убедитесь, что указали верный путь к интерпретатору, то есть он действительно лежит в /usr/bin/python

Ну и права на запуск файла должны быть верно выставлены, конечно. Удачи.

понедельник, 18 августа 2014 г.

Wall.post и open api access error

Римлянцы, совграждане, товарищи дорогие! Если вы с вашего сайтика делаете запрос вконтакт, пытаясь запостить картинку пользователю на стену, а он вам отвечает "open api access error", проверьте, добавлен ли ваш домен в список доменов приложения вконтакте.

Сам же запрос элементарен:
img = 'photo-11111_1111'; //id картинки, уже загруженной вконтакт, можно найти в её url
VK.Api.call('wall.post', { attachments:img}, function(r) {
//callback
});

Всем бобра!

воскресенье, 25 мая 2014 г.

Пишем макрос для OpenOffice Writer на Python. Вставляем изображения.

Да-а, ребята, это было непросто. Документация, конечно, есть, но в ней чёрт ногу сломит, а большая часть примеров вовсе даже на Basic. А мы вот попробуем something completely different - на Питоне.

ТЗ: Имеется папка, в ней тыща миллион до неба картинок jpg. Картинки требуется вставлять попарно в документ OpenOffice Writer между абзацами текста. То есть поставил курсор - воткнул следующую пару картинок - поехал дальше. Посмотрим, что мы можем сделать.

Прежде всего: питоновские скрипты не редактируются в самом OpenOffice. Редактируем их в чём угодно и загружаем в специальную папку - при стандартной установке на Windows это C:\Program Files\OpenOffice 4\share\Scripts\python . Там должны уже лежать встроенные скриптики типа HelloWorld.py.

ОК, что же нужно написать в нашем файле, чтобы сделать красиво? Разберём построчно.

Подгружаем нужные библиотеки
from com.sun.star.awt import Size
from PIL import Image #Если вы придумаете, как по-другому определять размер картинки, можно эту строчку пропустить. Если нет, то нужно скачать библиотеку PIL (гугл в помощь) и загрузить в папку питона в OpenOffice: C:\Program Files\OpenOffice 4\program\python-core-2.7.6\lib\site-packages

Тут у нас четыре функции. При вызове макроса исполняется последняя, первые три - вспомогательные.

def AddGraphic(pbreak):
st = getImagePath() # Вторая вспомогательная функция. Вторая и третья нужны для того, чтобы последовательно переходить от картинки к картинке в заданной папке.
oDoc = XSCRIPTCONTEXT.getDocument() # Получаем открытый документ
oText = oDoc.Text #Получаем текст документа
VC = oDoc.CurrentController.ViewCursor #Получаем объект курсора

GraphObj = oDoc.createInstance("com.sun.star.text.GraphicObject") # Создаём картинку
GraphObj.GraphicURL = 'file:///' + st # Источник картинки - наш очередной файл. Не забываем про приставку file:///

img = Image.open(st) #В следующих строчках мы при помощи библиотеки PIL получаем размер изображения, чтобы правильно смасштабировать его высоту при заданной ширине. Вообще-то предполагается, что у GraphicObject есть свойство ActualSize, в документации оно вроде как прописано, но на деле у меня ругалось, что такого свойства нет - пришлось выкручиваться.
if img.size[1] > 0:
prop = float(img.size[0])/float(img.size[1])
imgsize = Size(8500, (8500 / prop) ); # 8500 - это чуть меньше половины ширины страницы (8.5 см) - я нагло и беспардонно посмотрела ширину, потому что искать, как высчитать её программно, уже не было времени и сил. Если у вас получится, отпишитесь в комменты))
GraphObj.setSize(imgsize)
                 # Если это первая картинка, выравниваем по левому краю, если вторая - по правому
if(pbreak == 1):                  
      GraphObj.HoriOrient = 1
else:
       GraphObj.HoriOrient = 0
# Вставляем изображение
oText.insertTextContent(VC.Start, GraphObj, False)
               # Если это вторая картинка, создаём новый абзац
if(pbreak == 1):
oText.insertControlCharacter(VC.Start, 0, False)
#Есть мнение, что после всего этого нужно обновить документ. Не заметила особых изменений, но пусть будет.
oDoc.refresh()

def getImagePath():   # У меня всё облегчается тем, что изображения пронумерованы, поэтому мы просто прибавляем цифирку к номеру. Впрочем, просто перебрать все картинки в папке особого труда тоже не составит.
    dir = 'D:/mymages/image-'
    type = '.jpg'
    num = getNumber()
    st = dir + num + type
    return st

def getNumber(): # Чтобы сохранить номер последней загруженной картинки, используем текстовый файл-счетчик
    file = "D:/counter.txt"
    num = 0
    f = open(file, 'r+')
    num = f.read()
    f.seek(0)
    newnum = str(int(num) + 1)
    f.write(newnum)
    f.close()
    return num

def InsertImage( ): #Два раза вызываем функцию вставки картинок, второй раз после этого переходим на новый абзац
    AddGraphic(0)
    AddGraphic(1)
    return None

# В качестве макроса нам нужна только основная функция, так и пишем.
g_exportedScripts = InsertImage

Скачать файл можно тут. На самом деле, даже нужно, это же Python, вряд ли вы захотите потом исправлять все отступы. Удачи!