Fetch notifications

This commit is contained in:
koalasat 2024-07-09 21:38:25 +02:00
parent 0e6ae63ac4
commit 7388b4f6de
14 changed files with 204 additions and 22 deletions

View File

@ -0,0 +1,25 @@
import os
import site
import sys
# First, drop system-sites related paths.
original_sys_path = sys.path[:]
known_paths = set()
for path in {"/usr/local/lib/python3.12/site-packages"}:
site.addsitedir(path, known_paths=known_paths)
system_paths = set(
os.path.normcase(path) for path in sys.path[len(original_sys_path) :]
)
original_sys_path = [
path for path in original_sys_path if os.path.normcase(path) not in system_paths
]
sys.path = original_sys_path
# Second, add lib directories.
# ensuring .pth file are processed.
for path in [
"/tmp/pip-build-env-wwuhobll/overlay/lib/python3.12/site-packages",
"/tmp/pip-build-env-wwuhobll/normal/lib/python3.12/site-packages",
]:
assert path not in sys.path
site.addsitedir(path)

View File

@ -0,0 +1 @@
git+https://github.com/Reckless-Satoshi/drf-openapi-tester.git@soften-django-requirements (from -r requirements_dev.txt (line 3))

@ -0,0 +1 @@
Subproject commit 2af4d20743439444054c5bf8a02c9f299b14491f

View File

@ -7,6 +7,7 @@ import EncryptedStorage from 'react-native-encrypted-storage';
import { name as app_name, version as app_version } from './package.json';
import TorModule from './native/TorModule';
import RoboIdentitiesModule from './native/RoboIdentitiesModule';
import NotificationsModule from './native/NotificationsModule';
const backgroundColors = {
light: 'white',
@ -62,6 +63,7 @@ const App = () => {
webViewRef.current?.injectJavaScript(
`(function() {window.NativeRobosats?.loadCookie(${json});})();`,
);
return value;
}
});
};
@ -72,7 +74,10 @@ const App = () => {
loadCookie('settings_light_qr');
loadCookie('settings_network');
loadCookie('settings_use_proxy');
loadCookie('garage_slots').then(() => injectMessageResolve(responseId));
loadCookie('garage_slots').then((slots) => {
NotificationsModule.monitorOrders(slots ?? '{}');
injectMessageResolve(responseId);
});
};
const onCatch = (dataId: string, event: any) => {

View File

@ -13,6 +13,7 @@ import androidx.work.WorkManager;
import com.facebook.react.ReactActivity;
import com.facebook.react.ReactActivityDelegate;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactRootView;
import com.robosats.workers.NotificationWorker;
@ -75,7 +76,7 @@ public class MainActivity extends ReactActivity {
// ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);
OneTimeWorkRequest workRequest =
new OneTimeWorkRequest.Builder(NotificationWorker.class)
.setInitialDelay(5, TimeUnit.SECONDS)
.setInitialDelay(25, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(getApplicationContext())

View File

@ -4,6 +4,7 @@ import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.robosats.modules.NotificationsModule;
import com.robosats.modules.RoboIdentitiesModule;
import com.robosats.modules.TorModule;
@ -23,6 +24,7 @@ public class RobosatsPackage implements ReactPackage {
List<NativeModule> modules = new ArrayList<>();
modules.add(new TorModule(reactContext));
modules.add(new NotificationsModule(reactContext));
modules.add(new RoboIdentitiesModule(reactContext));
return modules;

View File

@ -0,0 +1,30 @@
package com.robosats.modules;
import android.content.SharedPreferences;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class NotificationsModule extends ReactContextBaseJavaModule {
public NotificationsModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "NotificationsModule";
}
@ReactMethod
public void monitorOrders(String slots_json) {
String PREFS_NAME = "Notifications";
String KEY_DATA = "Slots";
SharedPreferences sharedPreferences = getReactApplicationContext().getSharedPreferences(PREFS_NAME, ReactApplicationContext.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(KEY_DATA, slots_json);
editor.apply();
}
}

View File

@ -1,5 +1,6 @@
package com.robosats.modules;
import android.app.Application;
import android.util.Log;
import androidx.annotation.NonNull;
@ -11,6 +12,7 @@ import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.robosats.tor.TorKmp;
import com.robosats.tor.TorKmpManager;
import org.json.JSONException;
@ -30,10 +32,11 @@ import okhttp3.Response;
public class TorModule extends ReactContextBaseJavaModule {
private TorKmpManager torKmpManager;
private ReactApplicationContext context;
public TorModule(ReactApplicationContext reactContext) {
context = reactContext;
TorKmp torKmpManager = new TorKmp((Application) context.getApplicationContext());
TorKmpManager.INSTANCE.updateTorKmpObject(torKmpManager);
}
@Override
@ -46,7 +49,8 @@ public class TorModule extends ReactContextBaseJavaModule {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) // Set connection timeout
.readTimeout(30, TimeUnit.SECONDS) // Set read timeout
.proxy(torKmpManager.getProxy()).build();
.proxy(TorKmpManager.INSTANCE.getTorKmpObject().getProxy())
.build();
Request.Builder requestBuilder = new Request.Builder().url(url);
@ -90,7 +94,7 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void getTorStatus() {
String torState = torKmpManager.getTorState().getState().name();
String torState = TorKmpManager.INSTANCE.getTorKmpObject().getTorState().getState().name();
WritableMap payload = Arguments.createMap();
payload.putString("torStatus", torState);
context
@ -100,7 +104,7 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void isConnected() {
String isConnected = String.valueOf(torKmpManager.isConnected());
String isConnected = String.valueOf(TorKmpManager.INSTANCE.getTorKmpObject().isConnected());
WritableMap payload = Arguments.createMap();
payload.putString("isConnected", isConnected);
context
@ -110,7 +114,7 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void isStarting() {
String isStarting = String.valueOf(torKmpManager.isStarting());
String isStarting = String.valueOf(TorKmpManager.INSTANCE.getTorKmpObject().isStarting());
WritableMap payload = Arguments.createMap();
payload.putString("isStarting", isStarting);
context
@ -120,7 +124,7 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void stop() {
torKmpManager.getTorOperationManager().stopQuietly();
TorKmpManager.INSTANCE.getTorKmpObject().getTorOperationManager().stopQuietly();
WritableMap payload = Arguments.createMap();
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
@ -129,8 +133,9 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void start() {
torKmpManager = new TorKmpManager(context.getCurrentActivity().getApplication());
torKmpManager.getTorOperationManager().startQuietly();
TorKmp torKmp = new TorKmp(context.getCurrentActivity().getApplication());
TorKmpManager.INSTANCE.updateTorKmpObject(torKmp);
TorKmpManager.INSTANCE.getTorKmpObject().getTorOperationManager().startQuietly();
WritableMap payload = Arguments.createMap();
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
@ -139,8 +144,9 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void restart() {
torKmpManager = new TorKmpManager(context.getCurrentActivity().getApplication());
torKmpManager.getTorOperationManager().restartQuietly();
TorKmp torKmp = new TorKmp(context.getCurrentActivity().getApplication());
TorKmpManager.INSTANCE.updateTorKmpObject(torKmp);
TorKmpManager.INSTANCE.getTorKmpObject().getTorOperationManager().restartQuietly();
WritableMap payload = Arguments.createMap();
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
@ -149,7 +155,7 @@ public class TorModule extends ReactContextBaseJavaModule {
@ReactMethod
public void newIdentity() {
torKmpManager.newIdentity(context.getCurrentActivity().getApplication());
TorKmpManager.INSTANCE.getTorKmpObject().newIdentity(context.getCurrentActivity().getApplication());
WritableMap payload = Arguments.createMap();
context
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)

View File

@ -28,7 +28,7 @@ import kotlinx.coroutines.*
import java.net.InetSocketAddress
import java.net.Proxy
class TorKmpManager(application : Application) {
class TorKmp(application : Application) {
private val TAG = "TorListener"
@ -387,3 +387,15 @@ class TorKmpManager(application : Application) {
}
}
}
object TorKmpManager {
private lateinit var torKmp: TorKmp
fun getTorKmpObject(): TorKmp {
return torKmp
}
fun updateTorKmpObject(newKmpObject: TorKmp) {
torKmp = newKmpObject
}
}

View File

@ -1,32 +1,123 @@
package com.robosats.workers;
import android.app.Application;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Build;
import android.content.SharedPreferences;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.core.app.NotificationCompat;
import androidx.work.Worker;
import androidx.work.WorkerParameters;
import com.facebook.react.bridge.ReactApplicationContext;
import com.robosats.R;
import com.robosats.tor.TorKmpManager;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.util.Iterator;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
public class NotificationWorker extends Worker {
private static final String CHANNEL_ID = "robosats_notifications";
private static final int NOTIFICATION_ID = 123;
private static final String PREFS_NAME = "Notifications";
private static final String KEY_DATA = "Slots";
public NotificationWorker(Context context, WorkerParameters params) {
super(context, params);
}
@Override
public Result doWork() {
displayNotification("Order #1111", "Test from the app");
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences(PREFS_NAME, ReactApplicationContext.MODE_PRIVATE);
String slotsJson = sharedPreferences.getString(KEY_DATA, null);
try {
assert slotsJson != null;
JSONObject slots = new JSONObject(slotsJson);
Iterator<String> it = slots.keys();
while (it.hasNext()) {
String robotToken = it.next();
JSONObject slot = (JSONObject) slots.get(robotToken);
JSONObject robots = slot.getJSONObject("robots");
String activeShortAlias = slot.getString("activeShortAlias");
JSONObject coordinatorRobot = robots.getJSONObject(activeShortAlias);
String coordinator = "satstralia";
fetchNotifications(coordinatorRobot, coordinator);
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
return Result.success();
}
private void displayNotification(String title, String message) {
private void fetchNotifications(JSONObject robot, String coordinator) throws JSONException {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) // Set connection timeout
.readTimeout(30, TimeUnit.SECONDS) // Set read timeout
.proxy(TorKmpManager.INSTANCE.getTorKmpObject().getProxy())
.build();
Request.Builder requestBuilder = new Request.Builder().url("http://satstraoq35jffvkgpfoqld32nzw2siuvowanruindbfojowpwsjdgad.onion/api/notifications");
requestBuilder.addHeader("Authorization", "Token " + robot.getString("tokenSHA256"));
requestBuilder.get();
Request request = requestBuilder.build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(@NonNull Call call, @NonNull IOException e) {
Log.d("RobosatsError", e.toString());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
String body = response.body() != null ? response.body().string() : "{}";
JSONObject headersJson = new JSONObject();
response.headers().names().forEach(name -> {
try {
headersJson.put(name, response.header(name));
} catch (JSONException e) {
throw new RuntimeException(e);
}
});
try {
JSONArray results = new JSONArray(body);
for (int i = 0; i < results.length(); i++) {
SharedPreferences sharedPreferences = getApplicationContext().getSharedPreferences(PREFS_NAME, ReactApplicationContext.MODE_PRIVATE);
JSONObject notification = results.getJSONObject(i);
Integer order_id = notification.getInt("order_id");
displayNotification(order_id, notification.getString("title"), coordinator);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString(coordinator + order_id, String.valueOf(notification.getInt("created_at")));
editor.apply();
}
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
});
}
private void displayNotification(Integer order_id, String message, String coordinator) {
NotificationManager notificationManager = (NotificationManager)
getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE);
@ -37,12 +128,12 @@ public class NotificationWorker extends Worker {
NotificationCompat.Builder builder =
new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID)
.setContentTitle(title)
.setContentTitle("Order #" + order_id)
.setContentText(message)
.setSmallIcon(R.mipmap.ic_launcher_round)
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
notificationManager.notify(NOTIFICATION_ID, builder.build());
notificationManager.notify(order_id, builder.build());
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,8 @@
import { NativeModules } from 'react-native';
const { NotificationsModule } = NativeModules;
interface NotificationsModuleInterface {
monitorOrders: (slotsJson: string) => void;
}
export default NotificationsModule as NotificationsModuleInterface;

View File

@ -2,8 +2,8 @@ import { NativeModules } from 'react-native';
const { RoboIdentitiesModule } = NativeModules;
interface RoboIdentitiesModuleInterface {
generateRoboname: (initialString: String) => Promise<string>;
generateRobohash: (initialString: String) => Promise<string>;
generateRoboname: (initialString: string) => Promise<string>;
generateRobohash: (initialString: string) => Promise<string>;
}
export default RoboIdentitiesModule as RoboIdentitiesModuleInterface;